<!DOCTYPE html>
<html lang="en">





<head>
  <meta charset="UTF-8">
  <link rel="apple-touch-icon" sizes="76x76" href="/img/favicon.jpg">
  <link rel="icon" type="image/png" href="/img/favicon.jpg">
  <meta name="viewport"
        content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no, shrink-to-fit=no">
  <meta http-equiv="x-ua-compatible" content="ie=edge">
  
  <meta name="theme-color" content="#2f4154">
  <meta name="description" content="">
  <meta name="author" content="John Doe">
  <meta name="keywords" content="">
  <title>Java并发 - Nyima&#39;s Blog</title>

  <link  rel="stylesheet" href="https://cdn.staticfile.org/twitter-bootstrap/4.4.1/css/bootstrap.min.css" />


  <link  rel="stylesheet" href="https://cdn.staticfile.org/github-markdown-css/4.0.0/github-markdown.min.css" />
  <link  rel="stylesheet" href="/lib/hint/hint.min.css" />

  
    <link  rel="stylesheet" href="https://cdn.staticfile.org/highlight.js/10.0.0/styles/github-gist.min.css" />
  

  


<!-- 主题依赖的图标库，不要自行修改 -->

<link rel="stylesheet" href="//at.alicdn.com/t/font_1749284_yg9cfy8wd6.css">



<link rel="stylesheet" href="//at.alicdn.com/t/font_1736178_pjno9b9zyxs.css">


<link  rel="stylesheet" href="/css/main.css" />

<!-- 自定义样式保持在最底部 -->


  <script  src="/js/utils.js" ></script>
<meta name="generator" content="Hexo 4.2.1"></head>


<body>
  <header style="height: 70vh;">
    <nav id="navbar" class="navbar fixed-top  navbar-expand-lg navbar-dark scrolling-navbar">
  <div class="container">
    <a class="navbar-brand"
       href="/">&nbsp;<strong>Nyima</strong>&nbsp;</a>

    <button id="navbar-toggler-btn" class="navbar-toggler" type="button" data-toggle="collapse"
            data-target="#navbarSupportedContent"
            aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
      <div class="animated-icon"><span></span><span></span><span></span></div>
    </button>

    <!-- Collapsible content -->
    <div class="collapse navbar-collapse" id="navbarSupportedContent">
      <ul class="navbar-nav ml-auto text-center">
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/">
                <i class="iconfont icon-home-fill"></i>
                Home
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/archives/">
                <i class="iconfont icon-archive-fill"></i>
                Archives
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/categories/">
                <i class="iconfont icon-category-fill"></i>
                Categories
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/tags/">
                <i class="iconfont icon-tags-fill"></i>
                Tags
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/links/">
                <i class="iconfont icon-link-fill"></i>
                Links
              </a>
            </li>
          
        
          
          
          
          
            <li class="nav-item">
              <a class="nav-link" href="/about/">
                <i class="iconfont icon-user-fill"></i>
                About
              </a>
            </li>
          
        
        
          <li class="nav-item" id="search-btn">
            <a class="nav-link" data-toggle="modal" data-target="#modalSearch">&nbsp;&nbsp;<i
                class="iconfont icon-search"></i>&nbsp;&nbsp;</a>
          </li>
        
      </ul>
    </div>
  </div>
</nav>

    <div class="view intro-2" id="background" parallax=true
         style="background: url('/img/3.jpg') no-repeat center center;
           background-size: cover;">
      <div class="full-bg-img">
        <div class="mask flex-center" style="background-color: rgba(0, 0, 0, 0.3)">
          <div class="container text-center white-text fadeInUp">
            <span class="h2" id="subtitle">
              
            </span>

            
              
  <div class="mt-3 post-meta">
    <i class="iconfont icon-date-fill" aria-hidden="true"></i>
    <time datetime="2020-06-08 10:00">
      June 8, 2020 am
    </time>
  </div>


<div class="mt-1">
  
    
    <span class="post-meta mr-2">
      <i class="iconfont icon-chart"></i>
      23.7k 字
    </span>
  

  
    
    <span class="post-meta mr-2">
      <i class="iconfont icon-clock-fill"></i>
      
      
      307
       分钟
    </span>
  

  
  
</div>

            
          </div>

          
        </div>
      </div>
    </div>
  </header>

  <main>
    
      

<div class="container-fluid">
  <div class="row">
    <div class="d-none d-lg-block col-lg-2"></div>
    <div class="col-lg-8 nopadding-md">
      <div class="container nopadding-md" id="board-ctn">
        <div class="py-5" id="board">
          <div class="post-content mx-auto" id="post">
            
            <article class="markdown-body">
              <h1 id="并发编程笔记"><a href="#并发编程笔记" class="headerlink" title="并发编程笔记"></a>并发编程笔记</h1><p>本博客根据<a href="https://www.bilibili.com/video/av81461839?from=search&seid=8445102345230304010" target="_blank" rel="noopener"><strong>黑马java并发编程教程</strong></a>学习而做的笔记，链接如下</p>
<h1 id="一、基本概念"><a href="#一、基本概念" class="headerlink" title="一、基本概念"></a>一、基本概念</h1><h2 id="1、进程与线程"><a href="#1、进程与线程" class="headerlink" title="1、进程与线程"></a>1、进程与线程</h2><h3 id="进程"><a href="#进程" class="headerlink" title="进程"></a>进程</h3><ul>
<li>程序由指令和数据组成，但这些指令要运行，数据要读写，就必须将指令加载至 CPU，数据加载至内存。在指令运行过程中还需要用到磁盘、网络等设备。进程就是用来加载指令、管理内存、管理 IO 的。</li>
<li>当一个程序被运行，从磁盘加载这个程序的代码至内存，这时就开启了一个进程。 </li>
<li>进程就可以视为程序的一个实例。大部分程序可以同时运行多个实例进程（例如记事本、画图、浏览器 等），也有的程序只能启动一个实例进程（例如网易云音乐、360 安全卫士等）</li>
</ul>
<h3 id="线程"><a href="#线程" class="headerlink" title="线程"></a>线程</h3><ul>
<li>一个进程之内可以分为一到多个线程。 </li>
<li>一个线程就是一个指令流，将指令流中的一条条指令以一定的顺序交给 CPU 执行 。</li>
<li>Java 中，线程作为小调度单位，进程作为资源分配的小单位。 在 windows 中进程是不活动的，只是作 为线程的容器</li>
</ul>
<h3 id="二者对比"><a href="#二者对比" class="headerlink" title="二者对比"></a>二者对比</h3><ul>
<li><p>进程基本上相互独立的，而线程存在于进程内，是进程的一个子集 进程拥有共享的资源，如内存空间等，供其内部的线程共享 </p>
<ul>
<li><p>进程间通信较为复杂 同一台计算机的进程通信称为 IPC（Inter-process communication） </p>
</li>
<li><p>不同计算机之间的进程通信，需要通过网络，并遵守共同的协议，例如 HTTP </p>
</li>
</ul>
</li>
<li><p>线程通信相对简单，因为它们共享进程内的内存，一个例子是多个线程可以访问同一个共享变量 线程更轻量，线程上下文切换成本一般上要比进程上下文切换低</p>
</li>
</ul>
<h4 id="进程和线程的切换"><a href="#进程和线程的切换" class="headerlink" title="进程和线程的切换"></a>进程和线程的切换</h4><p><strong>上下文切换</strong></p>
<p>内核为每一个进程维持一个上下文。<strong>上下文就是内核重新启动一个被抢占的进程所需的状态。</strong>包括以下内容：</p>
<ul>
<li>通用目的寄存器</li>
<li>浮点寄存器</li>
<li>程序计数器</li>
<li>用户栈</li>
<li>状态寄存器</li>
<li>内核栈</li>
<li>各种内核数据结构：比如描绘地址空间的<strong>页表</strong>，包含有关当前进程信息的<strong>进程表</strong>，以及包含进程已打开文件的信息的<strong>文件表</strong></li>
</ul>
<p><strong>进程切换和线程切换的主要区别</strong></p>
<p>最主要的一个区别在于<strong>进程切换涉及虚拟地址空间的切换而线程不会</strong>。因为每个进程都有自己的虚拟地址空间，而<strong>线程是共享所在进程的虚拟地址空间的</strong>，因此同一个进程中的线程进行线程切换时不涉及虚拟地址空间的转换</p>
<p>页表查找是一个很慢的过程，因此通常使用cache来缓存常用的地址映射，这样可以加速页表查找，这个cache就是快表TLB（translation Lookaside Buffer，用来加速页表查找）。由于每个进程都有自己的虚拟地址空间，那么显然每个进程都有自己的页表，那么<strong>当进程切换后页表也要进行切换，页表切换后TLB就失效了</strong>，cache失效导致命中率降低，那么虚拟地址转换为物理地址就会变慢，表现出来的就是程序运行会变慢，而线程切换则不会导致TLB失效，因为线程线程无需切换地址空间，因此我们通常说线程切换要比较进程切换快</p>
<p>而且还可能出现<strong>缺页中断</strong>，这就需要操作系统将需要的内容调入内存中，若内存已满则还需要将不用的内容调出内存，这也需要花费时间</p>
<p><strong>为什么TLB能加快访问速度</strong></p>
<p>快表可以避免每次都对页号进行地址的有效性判断。快表中保存了对应的物理块号，可以直接计算出物理地址，无需再进行有效性检查</p>
<h2 id="2、并发与并行"><a href="#2、并发与并行" class="headerlink" title="2、并发与并行"></a>2、并发与并行</h2><p>并发是一个CPU在不同的时间去不同线程中执行指令。</p>
<p>并行是多个CPU同时处理不同的线程。</p>
<p>引用 Rob Pike 的一段描述：</p>
<ul>
<li>并发（concurrent）是同一时间<strong>应对</strong>（dealing with）多件事情的能力 </li>
<li>并行（parallel）是同一时间<strong>动手做</strong>（doing）多件事情的能力</li>
</ul>
<h3 id="3、应用"><a href="#3、应用" class="headerlink" title="3、应用"></a>3、应用</h3><h4 id="应用之异步调用（案例1）"><a href="#应用之异步调用（案例1）" class="headerlink" title="应用之异步调用（案例1）"></a>应用之异步调用（案例1）</h4><p>以调用方角度来讲，如果</p>
<ul>
<li><p>需要等待结果返回，才能继续运行就是同步 </p>
</li>
<li><p>不需要等待结果返回，就能继续运行就是异步</p>
</li>
</ul>
<p>1) 设计<br>多线程可以让方法执行变为异步的（即不要巴巴干等着）比如说读取磁盘文件时，假设读取操作花费了 5 秒钟，如 果没有线程调度机制，这 5 秒 cpu 什么都做不了，其它代码都得暂停…<br>2) 结论 </p>
<ul>
<li><p>比如在项目中，视频文件需要转换格式等操作比较费时，这时开一个新线程处理视频转换，避免阻塞主线程 </p>
</li>
<li><p>tomcat 的异步 servlet 也是类似的目的，让用户线程处理耗时较长的操作，避免阻塞</p>
</li>
<li><p>tomcat 的工作线程 ui 程序中，开线程进行其他操作，避免阻塞 ui 线程</p>
</li>
</ul>
<p>结论 </p>
<ol>
<li><p>单核 cpu 下，多线程不能实际提高程序运行效率，只是为了能够在不同的任务之间切换，不同线程轮流使用 cpu ，不至于一个线程总占用 cpu，别的线程没法干活 </p>
</li>
<li><p>多核 cpu 可以并行跑多个线程，但能否提高程序运行效率还是要分情况的 </p>
<ul>
<li><p>有些任务，经过精心设计，将任务拆分，并行执行，当然可以提高程序的运行效率。但不是所有计算任 务都能拆分（参考后文的【阿姆达尔定律】）</p>
</li>
<li><p>也不是所有任务都需要拆分，任务的目的如果不同，谈拆分和效率没啥意义 </p>
</li>
</ul>
</li>
<li><p>IO 操作不占用 cpu，只是我们一般拷贝文件使用的是【阻塞 IO】，这时相当于线程虽然不用 cpu，但需要一 直等待 IO 结束，没能充分利用线程。所以才有后面的【非阻塞 IO】和【异步 IO】优化</p>
</li>
</ol>
<h1 id="二、线程的创建"><a href="#二、线程的创建" class="headerlink" title="二、线程的创建"></a>二、线程的创建</h1><h2 id="1、创建一个线程（非主线程）"><a href="#1、创建一个线程（非主线程）" class="headerlink" title="1、创建一个线程（非主线程）"></a>1、创建一个线程（非主线程）</h2><h3 id="方法一：通过继承Thread创建线程"><a href="#方法一：通过继承Thread创建线程" class="headerlink" title="方法一：通过继承Thread创建线程"></a>方法一：通过继承Thread创建线程</h3><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CreateThread</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		Thread myThread = <span class="hljs-keyword">new</span> MyThread();
        <span class="hljs-comment">// 启动线程</span>
		myThread.start();
	&#125;
&#125;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyThread</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Thread</span> </span>&#123;
	<span class="hljs-meta">@Override</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>&#123;
		System.out.println(<span class="hljs-string">"my thread running..."</span>);
	&#125;
&#125;</code></pre>

<p>使用继承方式的好处是，在run（）方法内获取当前线程直接使用this就可以了，无须使用Thread.currentThread（）方法；不好的地方是Java不支持多继承，如果继承了Thread类，那么就不能再继承其他类。另外任务与代码没有分离，当多个线程执行一样的任务时需要多份任务代码</p>
<h3 id="方法二：使用Runnable配合Thread-推荐"><a href="#方法二：使用Runnable配合Thread-推荐" class="headerlink" title="方法二：使用Runnable配合Thread(推荐)"></a>方法二：使用Runnable配合Thread(推荐)</h3><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Test2</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		<span class="hljs-comment">//创建线程任务</span>
		Runnable r = <span class="hljs-keyword">new</span> Runnable() &#123;
			<span class="hljs-meta">@Override</span>
			<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>&#123;
				System.out.println(<span class="hljs-string">"Runnable running"</span>);
			&#125;
		&#125;;
		<span class="hljs-comment">//将Runnable对象传给Thread</span>
		Thread t = <span class="hljs-keyword">new</span> Thread(r);
		<span class="hljs-comment">//启动线程</span>
		t.start();
	&#125;
&#125;</code></pre>

<p>或者</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">CreateThread2</span> </span>&#123;
   <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyRunnable</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Runnable</span> </span>&#123;

      <span class="hljs-meta">@Override</span>
      <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>&#123;
         System.out.println(<span class="hljs-string">"my runnable running..."</span>);
      &#125;
   &#125;

   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      MyRunnable myRunnable = <span class="hljs-keyword">new</span> MyRunnable();
      Thread thread = <span class="hljs-keyword">new</span> Thread(myRunnable);
      thread.start();
   &#125;
&#125;</code></pre>

<p>通过实现Runnable接口，并且实现run()方法。在创建线程时作为参数传入该类的实例即可</p>
<h4 id="方法二的简化：使用lambda表达式简化操作"><a href="#方法二的简化：使用lambda表达式简化操作" class="headerlink" title="方法二的简化：使用lambda表达式简化操作"></a>方法二的简化：使用lambda表达式简化操作</h4><p><strong>当一个接口带有@FunctionalInterface注解时，是可以使用lambda来简化操作的</strong></p>
<p>所以方法二中的代码可以被简化为</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Test2</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		<span class="hljs-comment">//创建线程任务</span>
		Runnable r = () -&gt; &#123;
            <span class="hljs-comment">//直接写方法体即可</span>
			System.out.println(<span class="hljs-string">"Runnable running"</span>);
			System.out.println(<span class="hljs-string">"Hello Thread"</span>);
		&#125;;
		<span class="hljs-comment">//将Runnable对象传给Thread</span>
		Thread t = <span class="hljs-keyword">new</span> Thread(r);
		<span class="hljs-comment">//启动线程</span>
		t.start();
	&#125;
&#125;</code></pre>

<p>可以再Runnable上使用Alt+Enter</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608144534.png" srcset="/img/loading.gif" alt=""></p>
<h4 id="原理之-Thread-与-Runnable-的关系"><a href="#原理之-Thread-与-Runnable-的关系" class="headerlink" title="原理之 Thread 与 Runnable 的关系"></a>原理之 Thread 与 Runnable 的关系</h4><p>分析 Thread 的源码，理清它与 Runnable 的关系<br><strong>小结</strong></p>
<ul>
<li>方法1 是把线程和任务合并在了一起</li>
<li>方法2 是把线程和任务分开了 </li>
<li>用 Runnable 更容易与线程池等高级 API 配合 用 Runnable 让任务类脱离了 Thread 继承体系，更灵活</li>
</ul>
<h3 id="方法三：使用FutureTask与Thread结合"><a href="#方法三：使用FutureTask与Thread结合" class="headerlink" title="方法三：使用FutureTask与Thread结合"></a>方法三：使用FutureTask与Thread结合</h3><p><strong>使用FutureTask可以用泛型指定线程的返回值类型（Runnable的run方法没有返回值）</strong></p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Test3</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> ExecutionException, InterruptedException </span>&#123;
        <span class="hljs-comment">//需要传入一个Callable对象</span>
		FutureTask&lt;Integer&gt; task = <span class="hljs-keyword">new</span> FutureTask&lt;Integer&gt;(<span class="hljs-keyword">new</span> Callable&lt;Integer&gt;() &#123;
			<span class="hljs-meta">@Override</span>
			<span class="hljs-function"><span class="hljs-keyword">public</span> Integer <span class="hljs-title">call</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception </span>&#123;
				System.out.println(<span class="hljs-string">"线程执行!"</span>);
				Thread.sleep(<span class="hljs-number">1000</span>);
				<span class="hljs-keyword">return</span> <span class="hljs-number">100</span>;
			&#125;
		&#125;);

		Thread r1 = <span class="hljs-keyword">new</span> Thread(task, <span class="hljs-string">"t2"</span>);
		r1.start();
		<span class="hljs-comment">//获取线程中方法执行后的返回结果</span>
		System.out.println(task.get());
	&#125;
&#125;</code></pre>

<p>或</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">UseFutureTask</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> ExecutionException, InterruptedException </span>&#123;
      FutureTask&lt;String&gt; futureTask = <span class="hljs-keyword">new</span> FutureTask&lt;&gt;(<span class="hljs-keyword">new</span> MyCall());
      Thread thread = <span class="hljs-keyword">new</span> Thread(futureTask);
      thread.start();
      <span class="hljs-comment">// 获得线程运行后的返回值</span>
      System.out.println(futureTask.get());
   &#125;
&#125;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyCall</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Callable</span>&lt;<span class="hljs-title">String</span>&gt; </span>&#123;
   <span class="hljs-meta">@Override</span>
   <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">call</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception </span>&#123;
      <span class="hljs-keyword">return</span> <span class="hljs-string">"hello world"</span>;
   &#125;
&#125;</code></pre>



<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>使用<strong>继承方式的好处是方便传参</strong>，你可以在子类里面添加成员变量，通过set方法设置参数或者通过构造函数进行传递，而如果使用Runnable方式，则只能使用主线程里面被声明为final的变量。<strong>不好的地方是Java不支持多继承</strong>，如果继承了Thread类，那么子类不能再继承其他类，而Runable则没有这个限制。<strong>前两种方式都没办法拿到任务的返回结果，但是Futuretask方式可以</strong></p>
<h2 id="2、原理之线程运行"><a href="#2、原理之线程运行" class="headerlink" title="2、原理之线程运行"></a>2、原理之线程运行</h2><h4 id="栈与栈帧"><a href="#栈与栈帧" class="headerlink" title="栈与栈帧"></a>栈与栈帧</h4><p>Java Virtual Machine Stacks （Java 虚拟机栈） 我们都知道 JVM 中由堆、栈、方法区所组成，其中栈内存是给谁用的呢？</p>
<ul>
<li>其实就是线程，每个线程启动后，虚拟机就会为其分配一块<strong>栈内存</strong></li>
<li>每个栈由多个栈帧（Frame）组成，对应着每次<strong>方法调用时所占用的内存</strong> </li>
<li>每个线程只能有一个活动栈帧，对应着当前正在执行的那个方法</li>
</ul>
<h4 id="线程上下文切换（Thread-Context-Switch）"><a href="#线程上下文切换（Thread-Context-Switch）" class="headerlink" title="线程上下文切换（Thread Context Switch）"></a>线程上下文切换（Thread Context Switch）</h4><p>因为以下一些原因导致 cpu 不再执行当前的线程，转而执行另一个线程的代码</p>
<ul>
<li>线程的 cpu 时间片用完</li>
<li>垃圾回收 有更高优先级的线程需要运行</li>
<li>线程自己调用了 sleep、yield、wait、join、park、synchronized、lock 等方法 </li>
</ul>
<p>当 Context Switch 发生时，需要由操作系统保存当前线程的状态，并恢复另一个线程的状态，Java 中对应的概念 就是程序计数器（Program Counter Register），它的作用是记住下一条 jvm 指令的执行地址，是线程私有的</p>
<ul>
<li>状态包括程序计数器、虚拟机栈中每个栈帧的信息，如局部变量、操作数栈、返回地址等</li>
<li>Context Switch 频繁发生会影响性能 </li>
</ul>
<h2 id="3、常用方法"><a href="#3、常用方法" class="headerlink" title="3、常用方法"></a>3、常用方法</h2><h3 id="1-start-vs-run"><a href="#1-start-vs-run" class="headerlink" title="(1)start() vs run()"></a>(1)start() vs run()</h3><p>  被创建的Thread对象直接调用重写的run方法时， run方法是在<strong>主线程</strong>中被执行的，而不是在我们所创建的线程中执行。所以如果想要在所创建的线程中执行run方法，<strong>需要使用Thread对象的start方法。</strong></p>
<h3 id="2-sleep-与yield"><a href="#2-sleep-与yield" class="headerlink" title="(2)sleep()与yield()"></a>(2)sleep()与yield()</h3><h4 id="sleep-使线程阻塞"><a href="#sleep-使线程阻塞" class="headerlink" title="sleep (使线程阻塞)"></a><strong>sleep</strong> (使线程阻塞)</h4><ol>
<li><p>调用 sleep 会让当前线程从 <strong>Running  进入 Timed Waiting 状态（阻塞）</strong>，可通过state()方法查看</p>
</li>
<li><p>其它线程可以使用  <strong>interrupt</strong> 方法打断正在睡眠的线程，这时 sleep 方法会抛出 InterruptedException</p>
</li>
<li><p>睡眠结束后的线程未必会立刻得到执行</p>
</li>
<li><p>建议用 <strong>TimeUnit 的 sleep</strong> 代替 Thread 的 sleep 来获得更好的可读性 。如：</p>
<pre><code class="hljs java"><span class="hljs-comment">//休眠一秒</span>
TimeUnit.SECONDS.sleep(<span class="hljs-number">1</span>);
<span class="hljs-comment">//休眠一分钟</span>
TimeUnit.MINUTES.sleep(<span class="hljs-number">1</span>);</code></pre>

</li>
</ol>
<h4 id="yield-（让出当前线程）"><a href="#yield-（让出当前线程）" class="headerlink" title="yield （让出当前线程）"></a>yield （让出当前线程）</h4><ol>
<li>调用 yield 会让当前线程从 <strong>Running 进入 Runnable  就绪状态</strong>（仍然有可能被执行），然后调度执行其它线程 </li>
<li>具体的实现依赖于操作系统的任务调度器</li>
</ol>
<h4 id="线程优先级"><a href="#线程优先级" class="headerlink" title="线程优先级"></a>线程优先级</h4><ul>
<li><p>线程优先级会提示（hint）调度器优先调度该线程，但它仅仅是一个提示，调度器可以忽略它 </p>
</li>
<li><p>如果 cpu 比较忙，那么优先级高的线程会获得更多的时间片，但 cpu 闲时，优先级几乎没作用</p>
</li>
<li><p>设置方法：</p>
<pre><code class="hljs java">thread1.setPriority(Thread.MAX_PRIORITY); <span class="hljs-comment">//设置为优先级最高</span></code></pre>




</li>
</ul>
<h3 id="3-join-方法"><a href="#3-join-方法" class="headerlink" title="(3)join()方法"></a>(3)join()方法</h3><p>用于等待某个线程结束。哪个线程内调用join()方法，就等待哪个线程结束，然后再去执行其他线程。</p>
<p>如在主线程中调用ti.join()，则是主线程等待t1线程结束</p>
<pre><code class="hljs java">Thread thread = <span class="hljs-keyword">new</span> Thread();
<span class="hljs-comment">//等待thread线程执行结束</span>
thread.join();
<span class="hljs-comment">//最多等待1000ms,如果1000ms内线程执行完毕，则会直接执行下面的语句，不会等够1000ms</span>
thread.join(<span class="hljs-number">1000</span>);</code></pre>



<h3 id="4-interrupt-方法"><a href="#4-interrupt-方法" class="headerlink" title="(4)interrupt()方法"></a>(4)interrupt()方法</h3><p>用于打断<strong>阻塞</strong>(sleep wait join…)的线程。 处于阻塞状态的线程，CPU不会给其分配时间片。</p>
<ul>
<li>如果一个线程在在运行中被打断，打断标记会被置为true。 </li>
<li>如果是打断因sleep wait join方法而被阻塞的线程，会将打断标记置为false</li>
</ul>
<pre><code class="hljs java"><span class="hljs-comment">//用于查看打断标记，返回值被boolean类型</span>
t1.isInterrupted();</code></pre>

<p>正常运行的线程在被打断后，<strong>不会停止</strong>，会继续执行。如果要让线程在被打断后停下来，需要<strong>使用打断标记来判断</strong>。</p>
<pre><code class="hljs reasonml"><span class="hljs-keyword">while</span>(<span class="hljs-literal">true</span>) &#123;
    <span class="hljs-keyword">if</span>(<span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">Thread</span>.</span></span>current<span class="hljs-constructor">Thread()</span>.is<span class="hljs-constructor">Interrupted()</span>) &#123;
        break;
    &#125;
&#125;</code></pre>



<h5 id="interrupt方法的应用——两阶段终止模式"><a href="#interrupt方法的应用——两阶段终止模式" class="headerlink" title="interrupt方法的应用——两阶段终止模式"></a><strong>interrupt方法的应用</strong>——两阶段终止模式</h5><p>当我们在执行线程一时，想要终止线程二，这是就需要使用interrupt方法来<strong>优雅</strong>的停止线程二。</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608144553.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>代码</strong></p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Test7</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> InterruptedException </span>&#123;
		Monitor monitor = <span class="hljs-keyword">new</span> Monitor();
		monitor.start();
		Thread.sleep(<span class="hljs-number">3500</span>);
		monitor.stop();
	&#125;
&#125;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Monitor</span> </span>&#123;

	Thread monitor;

	<span class="hljs-comment">/**</span>
<span class="hljs-comment">	 * 启动监控器线程</span>
<span class="hljs-comment">	 */</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">start</span><span class="hljs-params">()</span> </span>&#123;
		<span class="hljs-comment">//设置线控器线程，用于监控线程状态</span>
		monitor = <span class="hljs-keyword">new</span> Thread() &#123;
			<span class="hljs-meta">@Override</span>
			<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>&#123;
				<span class="hljs-comment">//开始不停的监控</span>
				<span class="hljs-keyword">while</span> (<span class="hljs-keyword">true</span>) &#123;
                    <span class="hljs-comment">//判断当前线程是否被打断了</span>
					<span class="hljs-keyword">if</span>(Thread.currentThread().isInterrupted()) &#123;
						System.out.println(<span class="hljs-string">"处理后续任务"</span>);
                        <span class="hljs-comment">//终止线程执行</span>
						<span class="hljs-keyword">break</span>;
					&#125;
					System.out.println(<span class="hljs-string">"监控器运行中..."</span>);
					<span class="hljs-keyword">try</span> &#123;
						<span class="hljs-comment">//线程休眠</span>
						Thread.sleep(<span class="hljs-number">1000</span>);
					&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
						e.printStackTrace();
						<span class="hljs-comment">//如果是在休眠的时候被打断，不会将打断标记设置为true，这时要重新设置打断标记</span>
						Thread.currentThread().interrupt();
					&#125;
				&#125;
			&#125;
		&#125;;
		monitor.start();
	&#125;

	<span class="hljs-comment">/**</span>
<span class="hljs-comment">	 * 	用于停止监控器线程</span>
<span class="hljs-comment">	 */</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">stop</span><span class="hljs-params">()</span> </span>&#123;
		<span class="hljs-comment">//打断线程</span>
		monitor.interrupt();
	&#125;
&#125;</code></pre>



<h3 id="5-不推荐使用的打断方法"><a href="#5-不推荐使用的打断方法" class="headerlink" title="(5)不推荐使用的打断方法"></a>(5)不推荐使用的打断方法</h3><ul>
<li>stop方法   停止线程运行（可能造成共享资源无法被释放，其他线程无法使用这些共享资源）</li>
<li>suspend（暂停线程）/resume（恢复线程）方法</li>
</ul>
<h3 id="6-守护线程"><a href="#6-守护线程" class="headerlink" title="(6)守护线程"></a>(6)守护线程</h3><p>当JAVA进程中有多个线程在执行时，只有当所有非守护线程都执行完毕后，JAVA进程才会结束。<strong>但当非守护线程全部执行完毕后，守护线程无论是否执行完毕，也会一同结束。</strong></p>
<pre><code class="hljs crmsh">//将线程设置为守护线程, 默认为<span class="hljs-literal">false</span>
<span class="hljs-literal">monitor</span>.setDaemon(<span class="hljs-literal">true</span>);</code></pre>



<p><strong>守护线程的应用</strong></p>
<ul>
<li>垃圾回收器线程就是一种守护线程 </li>
<li>Tomcat 中的 Acceptor 和 Poller 线程都是守护线程，所以 Tomcat 接收到 shutdown 命令后，不会等 待它们处理完当前请求</li>
</ul>
<h2 id="4、线程的状态"><a href="#4、线程的状态" class="headerlink" title="4、线程的状态"></a>4、线程的状态</h2><h3 id="1-五种状态"><a href="#1-五种状态" class="headerlink" title="(1)五种状态"></a>(1)五种状态</h3><p>这是从 <strong>操作系统</strong> 层面来描述的</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608144606.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li><p>【初始状态】仅是在语言层面创建了线程对象，还未与操作系统线程关联（例如线程调用了start方法）</p>
</li>
<li><p>【可运行状态】（就绪状态）指该线程已经被创建（与操作系统线程关联），可以由 CPU 调度执行 </p>
</li>
<li><p>【运行状态】指获取了 CPU 时间片运行中的状态 </p>
<ul>
<li>当 CPU 时间片用完，会从【运行状态】转换至【可运行状态】，会导致线程的上下文切换 </li>
</ul>
</li>
<li><p>【阻塞状态】</p>
<ul>
<li>如果调用了阻塞 API，如 BIO 读写文件，这时该线程实际不会用到 CPU，会导致线程上下文切换，进入 【阻塞状态】 </li>
<li>等 BIO 操作完毕，会由操作系统唤醒阻塞的线程，转换至【可运行状态】</li>
<li>与【可运行状态】的区别是，对【阻塞状态】的线程来说只要它们一直不唤醒，调度器就一直不会考虑调度它们 </li>
</ul>
</li>
<li><p>【终止状态】表示线程已经执行完毕，生命周期已经结束，不会再转换为其它状态</p>
</li>
</ul>
<h3 id="2-六种状态"><a href="#2-六种状态" class="headerlink" title="(2)六种状态"></a>(2)六种状态</h3><p>这是从 <strong>Java API</strong> 层面来描述的<br>根据 Thread.State 枚举，分为六种状态</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608144621.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li><strong>NEW</strong>  线程刚被创建，但是还没有调用 start() 方法</li>
<li><strong>RUNNABLE</strong> 当调用了 start() 方法之后，注意，Java API 层面的 RUNNABLE 状态涵盖了操作系统层面的 【可运行状态】、【运行状态】和【阻塞状态】（由于 BIO 导致的线程阻塞，在 Java 里无法区分，仍然认为 是可运行） </li>
<li><strong>BLOCKED ， WAITING ， TIMED_WAITING</strong> 都是 <strong>Java API 层面</strong>对【阻塞状态】的细分，如sleep就位TIMED_WAITING， join为WAITING状态。后面会在状态转换一节详述。 </li>
<li><strong>TERMINATED</strong> 当线程代码运行结束</li>
</ul>
<h1 id="三、共享模型之管程"><a href="#三、共享模型之管程" class="headerlink" title="三、共享模型之管程"></a>三、共享模型之管程</h1><h2 id="1、共享带来的问题"><a href="#1、共享带来的问题" class="headerlink" title="1、共享带来的问题"></a>1、共享带来的问题</h2><h3 id="1-临界区-Critical-Section"><a href="#1-临界区-Critical-Section" class="headerlink" title="(1)临界区 Critical Section"></a>(1)临界区 Critical Section</h3><ul>
<li>一个程序运行多个线程本身是没有问题的</li>
<li>问题出在多个线程访问共享资源 <ul>
<li>多个线程读共享资源其实也没有问题 </li>
<li>在多个线程对共享资源读写操作时发生指令交错，就会出现问题 </li>
</ul>
</li>
<li>一段代码块内如果存在对共享资源的多线程读写操作，称这段代码块为<strong>临界区</strong><br>例如，下面代码中的临界区</li>
</ul>
<pre><code class="hljs java"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> counter = <span class="hljs-number">0</span>;
 
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">increment</span><span class="hljs-params">()</span> </span>
<span class="hljs-function"><span class="hljs-comment">// 临界区 </span></span>
<span class="hljs-function"></span>&#123;   
    counter++; 
&#125;
 
<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">decrement</span><span class="hljs-params">()</span> </span>
<span class="hljs-function"><span class="hljs-comment">// 临界区 </span></span>
<span class="hljs-function"></span>&#123; 
    counter--; 
&#125;</code></pre>



<h3 id="2-竞态条件-Race-Condition"><a href="#2-竞态条件-Race-Condition" class="headerlink" title="(2)竞态条件 Race Condition"></a><strong>(2)竞态条件 Race Condition</strong></h3><p>多个线程在<strong>临界区</strong>内执行，由于代码的执行序列不同而导致结果无法预测，称之为发生了<strong>竞态条件</strong></p>
<h2 id="2、synchronized-解决方案"><a href="#2、synchronized-解决方案" class="headerlink" title="2、synchronized 解决方案"></a>2、synchronized 解决方案</h2><h3 id="1-解决手段"><a href="#1-解决手段" class="headerlink" title="(1)解决手段"></a>(1)解决手段</h3><p>为了避免临界区的竞态条件发生，有多种手段可以达到目的。</p>
<ul>
<li>阻塞式的解决方案：synchronized，Lock </li>
<li>非阻塞式的解决方案：原子变量</li>
</ul>
<p>本次课使用阻塞式的解决方案：<strong>synchronized</strong>，来解决上述问题，即俗称的<strong>【对象锁】</strong>，它采用互斥的方式让同一 时刻至多只有一个线程能持有【对象锁】，其它线程再想获取这个【对象锁】时就会阻塞住(blocked)。这样就能保证拥有锁 的线程可以安全的执行临界区内的代码，不用担心线程上下文切换</p>
<h3 id="2-synchronized语法"><a href="#2-synchronized语法" class="headerlink" title="(2)synchronized语法"></a>(2)synchronized语法</h3><pre><code class="hljs java"><span class="hljs-keyword">synchronized</span>(对象) &#123;
	<span class="hljs-comment">//临界区</span>
&#125;</code></pre>



<p>例：</p>
<pre><code class="hljs java"><span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> counter = <span class="hljs-number">0</span>; 
<span class="hljs-comment">//创建一个公共对象，作为对象锁的对象</span>
<span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Object room = <span class="hljs-keyword">new</span> Object();
 
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> InterruptedException </span>&#123;    
	Thread t1 = <span class="hljs-keyword">new</span> Thread(() -&gt; &#123;        
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">5000</span>; i++) &#123;            
        <span class="hljs-keyword">synchronized</span> (room) &#123;     
        counter++;            
       	 &#125;       
 	   &#125;    
    &#125;, <span class="hljs-string">"t1"</span>);
 
    Thread t2 = <span class="hljs-keyword">new</span> Thread(() -&gt; &#123;       
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">5000</span>; i++) &#123;         
            <span class="hljs-keyword">synchronized</span> (room) &#123;            
            counter--;          
            &#125;    
        &#125; 
    &#125;, <span class="hljs-string">"t2"</span>);
 
    t1.start();    
    t2.start(); 
    t1.join();   
    t2.join();    
    log.debug(<span class="hljs-string">"&#123;&#125;"</span>,counter); 
&#125;</code></pre>



<h3 id="3-synchronized加在方法上"><a href="#3-synchronized加在方法上" class="headerlink" title="(3)synchronized加在方法上"></a>(3)synchronized加在方法上</h3><ul>
<li><p>加在成员方法上</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo</span> </span>&#123;
	<span class="hljs-comment">//在方法上加上synchronized关键字</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">void</span> <span class="hljs-title">test</span><span class="hljs-params">()</span> </span>&#123;
	
	&#125;
	<span class="hljs-comment">//等价于</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">test</span><span class="hljs-params">()</span> </span>&#123;
		<span class="hljs-keyword">synchronized</span>(<span class="hljs-keyword">this</span>) &#123;
		
		&#125;
	&#125;
&#125;</code></pre>
</li>
<li><p>加在静态方法上</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo</span> </span>&#123;
	<span class="hljs-comment">//在静态方法上加上synchronized关键字</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">test</span><span class="hljs-params">()</span> </span>&#123;
	
	&#125;
	<span class="hljs-comment">//等价于</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">test</span><span class="hljs-params">()</span> </span>&#123;
		<span class="hljs-keyword">synchronized</span>(Demo<span class="hljs-class">.<span class="hljs-keyword">class</span>) </span>&#123;
		
		&#125;
	&#125;
&#125;</code></pre>




</li>
</ul>
<h2 id="3、变量的线程安全分析"><a href="#3、变量的线程安全分析" class="headerlink" title="3、变量的线程安全分析"></a>3、变量的线程安全分析</h2><h4 id="成员变量和静态变量是否线程安全？"><a href="#成员变量和静态变量是否线程安全？" class="headerlink" title="成员变量和静态变量是否线程安全？"></a>成员变量和静态变量是否线程安全？</h4><ul>
<li><p>如果它们没有共享，则线程安全</p>
</li>
<li><p>如果它们被共享了，根据它们的状态是否能够改变，又分两种情况</p>
<ul>
<li><p>如果只有读操作，则线程安全 </p>
</li>
<li><p>如果有读写操作，则这段代码是临界区，需要考虑线程安全</p>
</li>
</ul>
</li>
</ul>
<h4 id="局部变量是否线程安全？"><a href="#局部变量是否线程安全？" class="headerlink" title="局部变量是否线程安全？"></a>局部变量是否线程安全？</h4><ul>
<li>局部变量是线程安全的</li>
<li>但局部变量引用的对象则未必 （要看该对象<strong>是否被共享</strong>且被执行了读写操作）<ul>
<li>如果该对象没有逃离方法的作用范围，它是线程安全的</li>
<li>如果该对象逃离方法的作用范围，需要考虑线程安全</li>
</ul>
</li>
</ul>
<ul>
<li>局部变量是线程安全的——每个方法都在对应线程的栈中创建栈帧，不会被其他线程共享</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608144636.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li>如果调用的对象被共享，且执行了读写操作，则<strong>线程不安全</strong></li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608144649.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li>如果是局部变量，则会在堆中创建对应的对象，不会存在线程安全问题。</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608144702.png" srcset="/img/loading.gif" alt=""></p>
<h3 id="常见线程安全类"><a href="#常见线程安全类" class="headerlink" title="常见线程安全类"></a>常见线程安全类</h3><ul>
<li>String</li>
<li>Integer</li>
<li>StringBuﬀer </li>
<li>Random</li>
<li>Vector （List的线程安全实现类）</li>
<li>Hashtable （Hash的线程安全实现类）</li>
<li>java.util.concurrent 包下的类</li>
</ul>
<p>这里说它们是线程安全的是指，多个线程调用它们<strong>同一个实例的某个方法时</strong>，是线程安全的</p>
<ul>
<li><p>它们的每个方法是原子的（都被加上了synchronized）</p>
</li>
<li><p>但注意它们<strong>多个方法的组合不是原子的</strong>，所以可能会出现线程安全问题</p>
</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608144903.png" srcset="/img/loading.gif" alt=""></p>
<h3 id="不可变类线程安全性"><a href="#不可变类线程安全性" class="headerlink" title="不可变类线程安全性"></a>不可变类线程安全性</h3><p>String、Integer 等都是<strong>不可变类</strong>，因为其内部的状态不可以改变，因此它们的方法都是线程安全的 </p>
<p>有同学或许有疑问，String 有 replace，substring 等方法【可以】改变值啊，那么这些方法又是如何保证线程安 全的呢？</p>
<p>这是因为这些方法的返回值都<strong>创建了一个新的对象</strong>，而不是直接改变String、Integer对象本身。</p>
<h2 id="4、Monitor概念"><a href="#4、Monitor概念" class="headerlink" title="4、Monitor概念"></a>4、Monitor概念</h2><h3 id="1-原理之Monitor"><a href="#1-原理之Monitor" class="headerlink" title="(1)原理之Monitor"></a>(1)原理之Monitor</h3><p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608144917.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li><p>当线程执行到临界区代码时，如果使用了synchronized，会先查询synchronized中所指定的对象(obj)<strong>是否绑定了Monitor</strong>。</p>
<ul>
<li>如果<strong>没有绑定</strong>，则会先去去与Monitor绑定，并且将Owner设为当前线程。</li>
<li>如果<strong>已经绑定</strong>，则会去查询该Monitor是否已经有了Owner<ul>
<li>如果没有，则Owner与将当前线程绑定</li>
<li>如果有，则放入EntryList，进入阻塞状态(blocked)</li>
</ul>
</li>
</ul>
</li>
<li><p>当Monitor的Owner将临界区中代码执行完毕后，Owner便会被清空，此时EntryList中处于<strong>阻塞</strong>状态的线程会被<strong>叫醒并竞争</strong>，此时的竞争是<strong>非公平的</strong></p>
</li>
<li><p><strong>注意</strong>：</p>
<ul>
<li><p>对象在使用了synchronized后与Monitor绑定时，会将对象头中的<strong>Mark Word</strong>置为Monitor指针。</p>
</li>
<li><p>每个对象都会绑定一个<strong>唯一的Monitor</strong>，如果synchronized中所指定的对象(obj)<strong>不同</strong>，则会绑定<strong>不同</strong>的Monitor</p>
</li>
</ul>
</li>
</ul>
<h2 id="5、Synchronized原理进阶"><a href="#5、Synchronized原理进阶" class="headerlink" title="5、Synchronized原理进阶"></a>5、Synchronized原理进阶</h2><h3 id="对象头格式"><a href="#对象头格式" class="headerlink" title="对象头格式"></a>对象头格式</h3><p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608144926.png" srcset="/img/loading.gif" alt=""></p>
<h3 id="1-轻量级锁（用于优化Monitor这类的重量级锁）"><a href="#1-轻量级锁（用于优化Monitor这类的重量级锁）" class="headerlink" title="(1)轻量级锁（用于优化Monitor这类的重量级锁）"></a>(1)轻量级锁（用于优化Monitor这类的重量级锁）</h3><p><strong>轻量级锁使用场景：</strong>当一个对象被多个线程所访问，但访问的时间是<strong>错开的（不存在竞争）</strong>，此时就可以使用<strong>轻量级锁</strong>来优化。</p>
<ul>
<li><p>创建<strong>锁记录</strong>（Lock Record）对象，每个线程的栈帧都会包含一个锁记录对象，内部可以存储锁定对象的mark word（不再一开始就使用Monitor）</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608144942.png" srcset="/img/loading.gif" alt=""></p>
</li>
<li><p>让锁记录中的Object reference指向锁对象（Object），并尝试用cas去替换Object中的mark word，将此mark word放入lock record中保存</p>
</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608144950.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li>如果cas替换成功，则将Object的对象头替换为<strong>锁记录的地址</strong>和<strong>状态 00（轻量级锁状态）</strong>，并由该线程给对象加锁</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608144957.png" srcset="/img/loading.gif" alt=""></p>
<h3 id="2-锁膨胀"><a href="#2-锁膨胀" class="headerlink" title="(2)锁膨胀"></a>(2)锁膨胀</h3><ul>
<li>如果一个线程在给一个对象加轻量级锁时，<strong>cas替换操作失败</strong>（因为此时其他线程已经给对象加了轻量级锁），此时该线程就会进入<strong>锁膨胀</strong>过程</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145004.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li><p>此时便会给对象加上重量级锁（使用Monitor）</p>
<ul>
<li><p>将对象头的Mark Word改为Monitor的地址，并且状态改为01(重量级锁)</p>
</li>
<li><p>并且该线程放入入EntryList中，并进入阻塞状态(blocked)</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145148.png" srcset="/img/loading.gif" alt=""></p>
</li>
</ul>
</li>
</ul>
<h3 id="3-自旋优化"><a href="#3-自旋优化" class="headerlink" title="(3)自旋优化"></a>(3)自旋优化</h3><p><strong>重量级锁</strong>竞争时，还可以使用自选来优化，如果当前线程在<strong>自旋成功</strong>（使用锁的线程退出了同步块，<strong>释放了锁</strong>），这时就可以避免线程进入阻塞状态。</p>
<ul>
<li>第一种情况</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145136.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li>第二种情况</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145125.png" srcset="/img/loading.gif" alt=""></p>
<h3 id="4-偏向锁-用于优化轻量级锁重入"><a href="#4-偏向锁-用于优化轻量级锁重入" class="headerlink" title="(4)偏向锁(用于优化轻量级锁重入)"></a>(4)偏向锁(用于优化轻量级锁重入)</h3><p>轻量级锁在没有竞争时，每次<strong>重入</strong>（该线程执行的方法中再次锁住该对象）操作仍需要cas替换操作，这样是会使性能降低的。</p>
<p>所以引入了<strong>偏向锁</strong>对性能进行优化：在<strong>第一次</strong>cas时会将<strong>线程的ID</strong>写入对象的Mark Word中。此后发现这个线程ID就是自己的，就表示没有竞争，就不需要再次cas，以后只要不发生竞争，这个对象就归该线程所有。</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145109.png" srcset="/img/loading.gif" alt=""></p>
<h4 id="偏向状态"><a href="#偏向状态" class="headerlink" title="偏向状态"></a>偏向状态</h4><ul>
<li>Normal：一般状态，没有加任何锁，前面62位保存的是对象的信息，<strong>最后2位为状态（01），倒数第三位表示是否使用偏向锁（未使用：0）</strong></li>
<li>Biased：偏向状态，使用偏向锁，前面54位保存的当前线程的ID，<strong>最后2位为状态（01），倒数第三位表示是否使用偏向锁（使用：1）</strong></li>
<li>Lightweight：使用轻量级锁，前62位保存的是锁记录的指针，<strong>最后两位为状态（00）</strong></li>
<li>Heavyweight：使用重量级锁，前62位保存的是Monitor的地址指针，<strong>后两位为状态(10)</strong></li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145101.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li>如果开启了偏向锁（默认开启），在创建对象时，对象的Mark Word后三位应该是101</li>
<li>但是偏向锁默认是<strong>有延迟</strong>的，不会再程序一启动就生效，而是会在程序运行一段时间（几秒之后），才会对创建的对象设置为偏向状态</li>
<li>如果没有开启偏向锁，对象的Mark Word后三位应该是001</li>
</ul>
<h4 id="撤销偏向"><a href="#撤销偏向" class="headerlink" title="撤销偏向"></a>撤销偏向</h4><p>以下几种情况会使对象的偏向锁失效</p>
<ul>
<li>调用对象的hashCode方法</li>
<li>多个线程使用该对象</li>
<li><strong>调用了wait/notify方法</strong>（调用wait方法会导致锁膨胀而使用<strong>重量级锁</strong>）</li>
</ul>
<h3 id="5-批量重偏向"><a href="#5-批量重偏向" class="headerlink" title="(5)批量重偏向"></a>(5)批量重偏向</h3><ul>
<li>如果对象虽然被多个线程访问，但是线程间不存在竞争，这时偏向T1的对象仍有机会重新偏向T2<ul>
<li>重偏向会重置Thread ID</li>
</ul>
</li>
<li>当撤销超过20次后（超过阈值），JVM会觉得是不是偏向错了，这时会在给对象加锁时，重新偏向至加锁线程。</li>
</ul>
<h3 id="6-批量撤销"><a href="#6-批量撤销" class="headerlink" title="(6)批量撤销"></a>(6)批量撤销</h3><p>当撤销偏向锁的阈值超过40以后，就会将<strong>整个类的对象都改为不可偏向的</strong></p>
<h2 id="6、Wait-Notify"><a href="#6、Wait-Notify" class="headerlink" title="6、Wait/Notify"></a>6、Wait/Notify</h2><h3 id="1-原理"><a href="#1-原理" class="headerlink" title="(1)原理"></a>(1)原理</h3><p>​    <img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145204.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li>锁对象调用wait方法（obj.wait），就会使当前线程进入WaitSet中，变为WAITING状态。</li>
<li>处于BLOCKED和WAITING状态的线程都为<strong>阻塞</strong>状态，CPU都不会分给他们时间片。但是有所区别：<ul>
<li>BLOCKED状态的线程是在竞争对象时，发现Monitor的Owner已经是别的线程了，此时就会进入EntryList中，并处于BLOCKED状态</li>
<li>WAITING状态的线程是获得了对象的锁，但是自身因为某些原因需要进入阻塞状态时，锁对象调用了wait方法而进入了WaitSet中，处于WAITING状态</li>
</ul>
</li>
<li>BLOCKED状态的线程会在锁被释放的时候被唤醒，但是处于WAITING状态的线程只有被锁对象调用了notify方法(obj.notify/obj.notifyAll)，才会被唤醒。</li>
</ul>
<p><strong>注：只有当对象被锁以后，才能调用wait和notify方法</strong></p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Test1</span> </span>&#123;
	<span class="hljs-keyword">final</span> <span class="hljs-keyword">static</span> Object LOCK = <span class="hljs-keyword">new</span> Object();
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> InterruptedException </span>&#123;
        <span class="hljs-comment">//只有在对象被锁住后才能调用wait方法</span>
		<span class="hljs-keyword">synchronized</span> (LOCK) &#123;
			LOCK.wait();
		&#125;
	&#125;
&#125;</code></pre>



<h3 id="2-Wait与Sleep的区别"><a href="#2-Wait与Sleep的区别" class="headerlink" title="(2)Wait与Sleep的区别"></a>(2)Wait与Sleep的区别</h3><p><strong>不同点</strong></p>
<ul>
<li>Sleep是Thread类的静态方法，Wait是Object的方法，Object又是所有类的父类，所以所有类都有Wait方法。</li>
<li>Sleep在阻塞的时候不会释放锁，而Wait在阻塞的时候会释放锁</li>
<li>Sleep不需要与synchronized一起使用，而Wait需要与synchronized一起使用（对象被锁以后才能使用）</li>
</ul>
<p><strong>相同点</strong></p>
<ul>
<li>阻塞状态都为<strong>TIMED_WAITING</strong></li>
</ul>
<h3 id="3-优雅地使用wait-notify"><a href="#3-优雅地使用wait-notify" class="headerlink" title="(3)优雅地使用wait/notify"></a>(3)优雅地使用wait/notify</h3><p><strong>什么时候适合使用wait</strong></p>
<ul>
<li>当线程<strong>不满足某些条件</strong>，需要暂停运行时，可以使用wait。这样会将<strong>对象的锁释放</strong>，让其他线程能够继续运行。如果此时使用sleep，会导致所有线程都进入阻塞，导致所有线程都没法运行，直到当前线程sleep结束后，运行完毕，才能得到执行。</li>
</ul>
<p><strong>使用wait/notify需要注意什么</strong></p>
<ul>
<li>当有<strong>多个</strong>线程在运行时，对象调用了wait方法，此时这些线程都会进入WaitSet中等待。如果这时使用了<strong>notify</strong>方法，可能会造成<strong>虚假唤醒</strong>（唤醒的不是满足条件的等待线程），这时就需要使用<strong>notifyAll</strong>方法</li>
</ul>
<pre><code class="hljs java"><span class="hljs-keyword">synchronized</span> (LOCK) &#123;
	<span class="hljs-keyword">while</span>(<span class="hljs-comment">//不满足条件，一直等待，避免虚假唤醒) &#123;</span>
		LOCK.wait();
	&#125;
	<span class="hljs-comment">//满足条件后再运行</span>
&#125;

<span class="hljs-keyword">synchronized</span> (LOCK) &#123;
	<span class="hljs-comment">//唤醒所有等待线程</span>
	LOCK.notifyAll();
&#125;</code></pre>

<h2 id="7、模式之保护性暂停"><a href="#7、模式之保护性暂停" class="headerlink" title="7、模式之保护性暂停"></a>7、模式之保护性暂停</h2><h3 id="1-定义"><a href="#1-定义" class="headerlink" title="(1)定义"></a>(1)定义</h3><p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145223.png" srcset="/img/loading.gif" alt=""></p>
<h3 id="2-举例"><a href="#2-举例" class="headerlink" title="(2)举例"></a>(2)举例</h3><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Test2</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		String hello = <span class="hljs-string">"hello thread!"</span>;
		Guarded guarded = <span class="hljs-keyword">new</span> Guarded();
		<span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
			System.out.println(<span class="hljs-string">"想要得到结果"</span>);
			<span class="hljs-keyword">synchronized</span> (guarded) &#123;
				System.out.println(<span class="hljs-string">"结果是："</span>+guarded.getResponse());
			&#125;
			System.out.println(<span class="hljs-string">"得到结果"</span>);
		&#125;).start();

		<span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
			System.out.println(<span class="hljs-string">"设置结果"</span>);
			<span class="hljs-keyword">synchronized</span> (guarded) &#123;
				guarded.setResponse(hello);
			&#125;
		&#125;).start();
	&#125;
&#125;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Guarded</span> </span>&#123;
	<span class="hljs-comment">/**</span>
<span class="hljs-comment">	 * 要返回的结果</span>
<span class="hljs-comment">	 */</span>
	<span class="hljs-keyword">private</span> Object response;
	
    <span class="hljs-comment">//优雅地使用wait/notify</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">getResponse</span><span class="hljs-params">()</span> </span>&#123;
		<span class="hljs-comment">//如果返回结果为空就一直等待，避免虚假唤醒</span>
		<span class="hljs-keyword">while</span>(response == <span class="hljs-keyword">null</span>) &#123;
			<span class="hljs-keyword">synchronized</span> (<span class="hljs-keyword">this</span>) &#123;
				<span class="hljs-keyword">try</span> &#123;
					<span class="hljs-keyword">this</span>.wait();
				&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
					e.printStackTrace();
				&#125;
			&#125;
		&#125;
		<span class="hljs-keyword">return</span> response;
	&#125;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setResponse</span><span class="hljs-params">(Object response)</span> </span>&#123;
		<span class="hljs-keyword">this</span>.response = response;
		<span class="hljs-keyword">synchronized</span> (<span class="hljs-keyword">this</span>) &#123;
			<span class="hljs-comment">//唤醒休眠的线程</span>
			<span class="hljs-keyword">this</span>.notifyAll();
		&#125;
	&#125;

	<span class="hljs-meta">@Override</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>&#123;
		<span class="hljs-keyword">return</span> <span class="hljs-string">"Guarded&#123;"</span> +
				<span class="hljs-string">"response="</span> + response +
				<span class="hljs-string">'&#125;'</span>;
	&#125;
&#125;</code></pre>



<p><strong>带超时判断的暂停</strong></p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> Object <span class="hljs-title">getResponse</span><span class="hljs-params">(<span class="hljs-keyword">long</span> time)</span> </span>&#123;
		<span class="hljs-keyword">synchronized</span> (<span class="hljs-keyword">this</span>) &#123;
			<span class="hljs-comment">//获取开始时间</span>
			<span class="hljs-keyword">long</span> currentTime = System.currentTimeMillis();
			<span class="hljs-comment">//用于保存已经等待了的时间</span>
			<span class="hljs-keyword">long</span> passedTime = <span class="hljs-number">0</span>;
			<span class="hljs-keyword">while</span>(response == <span class="hljs-keyword">null</span>) &#123;
				<span class="hljs-comment">//看经过的时间-开始时间是否超过了指定时间</span>
				<span class="hljs-keyword">long</span> waitTime = time -passedTime;
				<span class="hljs-keyword">if</span>(waitTime &lt;= <span class="hljs-number">0</span>) &#123;
					<span class="hljs-keyword">break</span>;
				&#125;
				<span class="hljs-keyword">try</span> &#123;
                   	<span class="hljs-comment">//等待剩余时间</span>
					<span class="hljs-keyword">this</span>.wait(waitTime);
				&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
					e.printStackTrace();
				&#125;
				<span class="hljs-comment">//获取当前时间</span>
				passedTime = System.currentTimeMillis()-currentTime		
            &#125;
		&#125;
		<span class="hljs-keyword">return</span> response;
	&#125;</code></pre>



<h3 id="3-join源码——使用保护性暂停模式"><a href="#3-join源码——使用保护性暂停模式" class="headerlink" title="(3)join源码——使用保护性暂停模式"></a>(3)join源码——使用保护性暂停模式</h3><pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">void</span> <span class="hljs-title">join</span><span class="hljs-params">(<span class="hljs-keyword">long</span> millis)</span></span>
<span class="hljs-function">    <span class="hljs-keyword">throws</span> InterruptedException </span>&#123;
        <span class="hljs-keyword">long</span> base = System.currentTimeMillis();
        <span class="hljs-keyword">long</span> now = <span class="hljs-number">0</span>;

        <span class="hljs-keyword">if</span> (millis &lt; <span class="hljs-number">0</span>) &#123;
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"timeout value is negative"</span>);
        &#125;

        <span class="hljs-keyword">if</span> (millis == <span class="hljs-number">0</span>) &#123;
            <span class="hljs-keyword">while</span> (isAlive()) &#123;
                wait(<span class="hljs-number">0</span>);
            &#125;
        &#125; <span class="hljs-keyword">else</span> &#123;
            <span class="hljs-keyword">while</span> (isAlive()) &#123;
                <span class="hljs-keyword">long</span> delay = millis - now;
                <span class="hljs-keyword">if</span> (delay &lt;= <span class="hljs-number">0</span>) &#123;
                    <span class="hljs-keyword">break</span>;
                &#125;
                wait(delay);
                now = System.currentTimeMillis() - base;
            &#125;
        &#125;
    &#125;</code></pre>

<h2 id="8、park-unpark"><a href="#8、park-unpark" class="headerlink" title="8、park/unpark"></a>8、park/unpark</h2><h3 id="1-基本使用"><a href="#1-基本使用" class="headerlink" title="(1)基本使用"></a>(1)基本使用</h3><p><strong>park/unpark都是LockSupport类中的的方法</strong></p>
<pre><code class="hljs java"><span class="hljs-comment">//暂停线程运行</span>
LockSupport.park;

<span class="hljs-comment">//恢复线程运行</span>
LockSupport.unpark(thread);</code></pre>



<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> InterruptedException </span>&#123;
		Thread thread = <span class="hljs-keyword">new</span> Thread(()-&gt; &#123;
			System.out.println(<span class="hljs-string">"park"</span>);
            <span class="hljs-comment">//暂停线程运行</span>
			LockSupport.park();
			System.out.println(<span class="hljs-string">"resume"</span>);
		&#125;, <span class="hljs-string">"t1"</span>);
		thread.start();

		Thread.sleep(<span class="hljs-number">1000</span>);
		System.out.println(<span class="hljs-string">"unpark"</span>);
    	<span class="hljs-comment">//恢复线程运行</span>
		LockSupport.unpark(thread);
	&#125;</code></pre>



<h3 id="2-特点"><a href="#2-特点" class="headerlink" title="(2)特点"></a>(2)特点</h3><p><strong>与wait/notify的区别</strong></p>
<ul>
<li>wait，notify 和 notifyAll 必须配合<strong>Object Monitor</strong>一起使用，而park，unpark不必</li>
<li>park ，unpark 是以<strong>线程为单位</strong>来<strong>阻塞</strong>和<strong>唤醒</strong>线程，而 notify 只能随机唤醒一个等待线程，notifyAll 是唤醒所有等待线程，就不那么精确</li>
<li>park &amp; unpark 可以<strong>先 unpark</strong>，而 wait &amp; notify 不能先 notify</li>
<li><strong>park不会释放锁</strong>，而wait会释放锁</li>
</ul>
<h3 id="3-原理"><a href="#3-原理" class="headerlink" title="(3)原理"></a>(3)原理</h3><p>每个线程都有一个自己的<strong>Park对象</strong>，并且该对象<strong>_counter, _cond,__mutex</strong>组成</p>
<ul>
<li><p>先调用park再调用unpark时</p>
<ul>
<li><p>先调用park</p>
<ul>
<li>线程运行时，会将Park对象中的<strong>_counter的值设为0</strong>；</li>
<li>调用park时，会先查看counter的值是否为0，如果为0，则将线程放入阻塞队列cond中</li>
<li>放入阻塞队列中后，会<strong>再次</strong>将counter设置为0</li>
</ul>
</li>
<li><p>然后调用unpark</p>
<ul>
<li><p>调用unpark方法后，会将counter的值设置为1</p>
</li>
<li><p>去唤醒阻塞队列cond中的线程</p>
</li>
<li><p>线程继续运行并将counter的值设为0</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145250.png" srcset="/img/loading.gif" alt=""></p>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145303.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li>先调用unpark，再调用park<ul>
<li>调用unpark<ul>
<li>会将counter设置为1（运行时0）</li>
</ul>
</li>
<li>调用park方法<ul>
<li>查看counter是否为0</li>
<li>因为unpark已经把counter设置为1，所以此时将counter设置为0，但<strong>不放入</strong>阻塞队列cond中</li>
</ul>
</li>
</ul>
</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145313.png" srcset="/img/loading.gif" alt=""></p>
<h2 id="9、线程中的状态转换"><a href="#9、线程中的状态转换" class="headerlink" title="9、线程中的状态转换"></a>9、线程中的状态转换</h2><p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145330.png" srcset="/img/loading.gif" alt=""></p>
<h3 id="情况一：NEW-–-gt-RUNNABLE"><a href="#情况一：NEW-–-gt-RUNNABLE" class="headerlink" title="情况一：NEW –&gt; RUNNABLE"></a>情况一：NEW –&gt; RUNNABLE</h3><ul>
<li>当调用了t.start()方法时，由 NEW –&gt; RUNNABLE </li>
</ul>
<h3 id="情况二：-RUNNABLE-lt-–-gt-WAITING"><a href="#情况二：-RUNNABLE-lt-–-gt-WAITING" class="headerlink" title="情况二： RUNNABLE &lt;–&gt; WAITING"></a>情况二： RUNNABLE &lt;–&gt; WAITING</h3><ul>
<li>当调用了t 线程用 synchronized(obj) 获取了对象锁后<ul>
<li>调用 obj.wait() 方法时，t 线程从 RUNNABLE –&gt; WAITING</li>
<li>调用 obj.notify() ， obj.notifyAll() ， t.interrupt() 时 <ul>
<li>竞争锁成功，t 线程从  WAITING –&gt; RUNNABLE </li>
<li>竞争锁失败，t 线程从  WAITING –&gt; BLOCKED </li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="情况三：RUNNABLE-lt-–-gt-WAITING"><a href="#情况三：RUNNABLE-lt-–-gt-WAITING" class="headerlink" title="情况三：RUNNABLE &lt;–&gt; WAITING"></a>情况三：RUNNABLE &lt;–&gt; WAITING</h3><ul>
<li><strong>当前线程</strong>调用 t.join() 方法时，当前线程从 RUNNABLE –&gt; WAITING<ul>
<li>注意是<strong>当前线程</strong>在t 线程对象的监视器上等待</li>
</ul>
</li>
<li>t 线程<strong>运行结束</strong>，或调用了<strong>当前线程</strong>的 interrupt() 时，当前线程从 WAITING –&gt; RUNNABLE</li>
</ul>
<h3 id="情况四：-RUNNABLE-lt-–-gt-WAITING"><a href="#情况四：-RUNNABLE-lt-–-gt-WAITING" class="headerlink" title="情况四： RUNNABLE &lt;–&gt; WAITING"></a>情况四： RUNNABLE &lt;–&gt; WAITING</h3><ul>
<li>当前线程调用 LockSupport.park() 方法会让当前线程从 RUNNABLE –&gt; WAITING</li>
<li>调用 LockSupport.unpark(目标线程) 或调用了线程 的 interrupt() ，会让目标线程从 WAITING –&gt; RUNNABLE</li>
</ul>
<h3 id="情况五：-RUNNABLE-lt-–-gt-TIMED-WAITING"><a href="#情况五：-RUNNABLE-lt-–-gt-TIMED-WAITING" class="headerlink" title="情况五： RUNNABLE &lt;–&gt; TIMED_WAITING"></a>情况五： RUNNABLE &lt;–&gt; TIMED_WAITING</h3><p>t 线程用 synchronized(obj) 获取了对象锁后</p>
<ul>
<li>调用 obj.wait(<strong>long n</strong>) 方法时，t 线程从 RUNNABLE –&gt; TIMED_WAITING</li>
<li>t 线程等待时间超过了 n 毫秒，或调用 obj.notify() ， obj.notifyAll() ， t.interrupt() 时<ul>
<li>竞争锁成功，t 线程从  TIMED_WAITING –&gt; RUNNABLE</li>
<li>竞争锁失败，t 线程从  TIMED_WAITING –&gt; BLOCKED </li>
</ul>
</li>
</ul>
<h3 id="情况六：RUNNABLE-lt-–-gt-TIMED-WAITING"><a href="#情况六：RUNNABLE-lt-–-gt-TIMED-WAITING" class="headerlink" title="情况六：RUNNABLE &lt;–&gt; TIMED_WAITING"></a>情况六：RUNNABLE &lt;–&gt; TIMED_WAITING</h3><ul>
<li>当前线程调用 t.join<strong>(long n</strong>) 方法时，当前线程从 RUNNABLE –&gt; TIMED_WAITING <ul>
<li>注意是当前线程在t 线程对象的监视器上等待 </li>
</ul>
</li>
<li>当前线程等待时间超过了 n 毫秒，或t 线程运行结束，或调用了当前线程的 interrupt() 时，当前线程从 TIMED_WAITING –&gt; RUNNABLE</li>
</ul>
<h3 id="情况七：RUNNABLE-lt-–-gt-TIMED-WAITING"><a href="#情况七：RUNNABLE-lt-–-gt-TIMED-WAITING" class="headerlink" title="情况七：RUNNABLE &lt;–&gt; TIMED_WAITING"></a>情况七：RUNNABLE &lt;–&gt; TIMED_WAITING</h3><ul>
<li>当前线程调用 Thread.sleep(long n) ，当前线程从 RUNNABLE –&gt; TIMED_WAITING </li>
<li>当前线程等待时间超过了 n 毫秒，当前线程从  TIMED_WAITING –&gt; RUNNABLE</li>
</ul>
<h3 id="情况八：RUNNABLE-lt-–-gt-TIMED-WAITING"><a href="#情况八：RUNNABLE-lt-–-gt-TIMED-WAITING" class="headerlink" title="情况八：RUNNABLE &lt;–&gt; TIMED_WAITING"></a>情况八：RUNNABLE &lt;–&gt; TIMED_WAITING</h3><ul>
<li>当前线程调用 LockSupport.parkNanos(long nanos) 或 LockSupport.parkUntil(long millis) 时，当前线 程从 RUNNABLE –&gt; TIMED_WAITING </li>
<li>调用 LockSupport.unpark(目标线程) 或调用了线程 的 interrupt() ，或是等待超时，会让目标线程从 TIMED_WAITING–&gt; RUNNABLE</li>
</ul>
<h3 id="情况九：RUNNABLE-lt-–-gt-BLOCKED"><a href="#情况九：RUNNABLE-lt-–-gt-BLOCKED" class="headerlink" title="情况九：RUNNABLE &lt;–&gt; BLOCKED"></a>情况九：RUNNABLE &lt;–&gt; BLOCKED</h3><ul>
<li>t 线程用  synchronized(obj) 获取了对象锁时如果<strong>竞争失败</strong>，从  RUNNABLE –&gt; BLOCKED </li>
<li>持 obj 锁线程的同步代码块执行完毕，会唤醒该对象上所有 BLOCKED  的线程重新竞争，如果其中 t 线程竞争 成功，从 BLOCKED –&gt; RUNNABLE ，其它<strong>失败</strong>的线程仍然  BLOCKED </li>
</ul>
<h3 id="情况十：-RUNNABLE-lt-–-gt-TERMINATED"><a href="#情况十：-RUNNABLE-lt-–-gt-TERMINATED" class="headerlink" title="情况十： RUNNABLE &lt;–&gt; TERMINATED"></a>情况十： RUNNABLE &lt;–&gt; TERMINATED</h3><p>当前线<strong>程所有代码运行完毕</strong>，进入 TERMINATED</p>
<h2 id="10、多把锁"><a href="#10、多把锁" class="headerlink" title="10、多把锁"></a>10、多把锁</h2><p><strong>将锁的粒度细分</strong></p>
<pre><code class="hljs java"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BigRoom</span> </span>&#123;
    <span class="hljs-comment">//额外创建对象来作为锁</span>
	<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Object studyRoom = <span class="hljs-keyword">new</span> Object();
	<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> Object bedRoom = <span class="hljs-keyword">new</span> Object();
&#125;</code></pre>



<h2 id="11、活跃性"><a href="#11、活跃性" class="headerlink" title="11、活跃性"></a>11、活跃性</h2><h3 id="1-定义-1"><a href="#1-定义-1" class="headerlink" title="(1)定义"></a>(1)定义</h3><p>因为某种原因，使得代码一直无法执行完毕，这样的现象叫做活跃性</p>
<h3 id="2-死锁"><a href="#2-死锁" class="headerlink" title="(2)死锁"></a>(2)死锁</h3><p>有这样的情况：一个线程需要<strong>同时获取多把锁</strong>，这时就容易发生死锁</p>
<p>如：t1线程获得A对象 锁，接下来想获取B对象的锁t2线程获得B对象锁，接下来想获取A对象的锁 </p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		<span class="hljs-keyword">final</span> Object A = <span class="hljs-keyword">new</span> Object();
		<span class="hljs-keyword">final</span> Object B = <span class="hljs-keyword">new</span> Object();
		<span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
			<span class="hljs-keyword">synchronized</span> (A) &#123;
				<span class="hljs-keyword">try</span> &#123;
					Thread.sleep(<span class="hljs-number">2000</span>);
				&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
					e.printStackTrace();
				&#125;
				<span class="hljs-keyword">synchronized</span> (B) &#123;

				&#125;
			&#125;
		&#125;).start();

		<span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
			<span class="hljs-keyword">synchronized</span> (B) &#123;
				<span class="hljs-keyword">try</span> &#123;
					Thread.sleep(<span class="hljs-number">1000</span>);
				&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
					e.printStackTrace();
				&#125;
				<span class="hljs-keyword">synchronized</span> (A) &#123;

				&#125;
			&#125;
		&#125;).start();
	&#125;</code></pre>



<h4 id="发生死锁的必要条件"><a href="#发生死锁的必要条件" class="headerlink" title="发生死锁的必要条件"></a>发生死锁的必要条件</h4><ul>
<li>互斥条件<ul>
<li>在一段时间内，一种资源只能被一个进程所使用</li>
</ul>
</li>
<li>请求和保持条件<ul>
<li>进程已经拥有了至少一种资源，同时又去申请其他资源。因为其他资源被别的进程所使用，该进程进入阻塞状态，并且不释放自己已有的资源</li>
</ul>
</li>
<li>不可抢占条件<ul>
<li>进程对已获得的资源在未使用完成前不能被强占，只能在进程使用完后自己释放</li>
</ul>
</li>
<li>循环等待条件<ul>
<li>发生死锁时，必然存在一个进程——资源的循环链。</li>
</ul>
</li>
</ul>
<h4 id="定位死锁的方法"><a href="#定位死锁的方法" class="headerlink" title="定位死锁的方法"></a>定位死锁的方法</h4><ul>
<li><p>jps+jstack ThreadID</p>
<ul>
<li><p>在JAVA控制台中的Terminal中输入<strong>jps</strong>指令可以查看运行中的线程ID，使用<strong>jstack ThreadID</strong>可以查看线程状态。</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145351.png" srcset="/img/loading.gif" alt=""></p>
</li>
</ul>
<pre><code class="hljs angelscript">F:\Thread_study&gt;jps
<span class="hljs-number">20672</span> RemoteMavenServer36
<span class="hljs-number">22880</span> Jps
<span class="hljs-number">4432</span> Launcher
<span class="hljs-number">5316</span> Test5
<span class="hljs-number">20184</span> KotlinCompileDaemon
<span class="hljs-number">11132</span>

F:\Thread_study&gt;jstack <span class="hljs-number">5316</span></code></pre>



</li>
</ul>
<ul>
<li><p>打印的结果</p>
<pre><code class="hljs java"><span class="hljs-comment">//找到一个java级别的死锁</span>
Found one Java-level deadlock:
=============================
<span class="hljs-string">"Thread-1"</span>:
  waiting to lock monitor <span class="hljs-number">0x0000000017f40de8</span> (object <span class="hljs-number">0x00000000d6188880</span>, a java.lang.Object),
  which is held by <span class="hljs-string">"Thread-0"</span>
<span class="hljs-string">"Thread-0"</span>:
  waiting to lock monitor <span class="hljs-number">0x0000000017f43678</span> (object <span class="hljs-number">0x00000000d6188890</span>, a java.lang.Object),
  which is held by <span class="hljs-string">"Thread-1"</span></code></pre>



</li>
</ul>
<ul>
<li><p>jconsole检测死锁</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145405.png" srcset="/img/loading.gif" alt=""></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145416.png" srcset="/img/loading.gif" alt=""></p>
</li>
</ul>
<h4 id="哲学家就餐问题"><a href="#哲学家就餐问题" class="headerlink" title="哲学家就餐问题"></a>哲学家就餐问题</h4><p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145436.png" srcset="/img/loading.gif" alt=""></p>
<h4 id="避免死锁的方法"><a href="#避免死锁的方法" class="headerlink" title="避免死锁的方法"></a>避免死锁的方法</h4><p>在线程使用锁对象时<strong>，顺序加锁</strong>即可避免死锁</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145450.png" srcset="/img/loading.gif" alt=""></p>
<h3 id="3-活锁"><a href="#3-活锁" class="headerlink" title="(3)活锁"></a>(3)活锁</h3><p>活锁出现在两个线程<strong>互相改变对方的结束条件</strong>，后谁也无法结束。</p>
<h4 id="避免活锁的方法"><a href="#避免活锁的方法" class="headerlink" title="避免活锁的方法"></a>避免活锁的方法</h4><p>在线程执行时，中途给予<strong>不同的间隔时间</strong>即可。</p>
<h4 id="死锁与活锁的区别"><a href="#死锁与活锁的区别" class="headerlink" title="死锁与活锁的区别"></a>死锁与活锁的区别</h4><ul>
<li>死锁是因为线程互相持有对象想要的锁，并且都不释放，最后到时<strong>线程阻塞</strong>，<strong>停止运行</strong>的现象。</li>
<li>活锁是因为线程间修改了对方的结束条件，而导致代码<strong>一直在运行</strong>，却一直<strong>运行不完</strong>的现象。</li>
</ul>
<h3 id="4-饥饿"><a href="#4-饥饿" class="headerlink" title="(4)饥饿"></a>(4)饥饿</h3><p>某些线程因为优先级太低，导致一直无法获得资源的现象。</p>
<p>在使用顺序加锁时，可能会出现饥饿现象</p>
<h2 id="12、ReentrantLock"><a href="#12、ReentrantLock" class="headerlink" title="12、ReentrantLock"></a>12、ReentrantLock</h2><p><strong>和synchronized相比具有的的特点</strong></p>
<ul>
<li>可中断 </li>
<li>可以设置超时时间 </li>
<li>可以设置为公平锁  (先到先得)</li>
<li>支持多个条件变量( 具有<strong>多个</strong>waitset)</li>
</ul>
<p><strong>基本语法</strong></p>
<pre><code class="hljs java"><span class="hljs-comment">//获取ReentrantLock对象</span>
<span class="hljs-keyword">private</span> ReentrantLock lock = <span class="hljs-keyword">new</span> ReentrantLock();
<span class="hljs-comment">//加锁</span>
lock.lock();
<span class="hljs-keyword">try</span> &#123;
	<span class="hljs-comment">//需要执行的代码</span>
&#125;<span class="hljs-keyword">finally</span> &#123;
	<span class="hljs-comment">//释放锁</span>
	lock.unlock();
&#125;</code></pre>



<h4 id="可重入"><a href="#可重入" class="headerlink" title="可重入"></a>可重入</h4><ul>
<li>可重入是指同一个线程如果首次获得了这把锁，那么因为它是这把锁的拥有者，因此有权利再次获取这把锁</li>
<li>如果是不可重入锁，那么第二次获得锁时，自己也会被锁挡住</li>
</ul>
<h4 id="可打断"><a href="#可打断" class="headerlink" title="可打断"></a>可打断</h4><p>如果某个线程处于阻塞状态，可以调用其interrupt方法让其停止阻塞，获得锁失败</p>
<p><strong>简而言之</strong>就是：处于阻塞状态的线程，被打断了就不用阻塞了，直接停止运行</p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		ReentrantLock lock = <span class="hljs-keyword">new</span> ReentrantLock();
		Thread t1 = <span class="hljs-keyword">new</span> Thread(()-&gt; &#123;
			<span class="hljs-keyword">try</span> &#123;
				<span class="hljs-comment">//加锁，可打断锁</span>
				lock.lockInterruptibly();
			&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
				e.printStackTrace();
                <span class="hljs-comment">//被打断，返回，不再向下执行</span>
				<span class="hljs-keyword">return</span>;
			&#125;<span class="hljs-keyword">finally</span> &#123;
				<span class="hljs-comment">//释放锁</span>
				lock.unlock();
			&#125;

		&#125;);

		lock.lock();
		<span class="hljs-keyword">try</span> &#123;
			t1.start();
			Thread.sleep(<span class="hljs-number">1000</span>);
			<span class="hljs-comment">//打断</span>
			t1.interrupt();
		&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
			e.printStackTrace();
		&#125; <span class="hljs-keyword">finally</span> &#123;
			lock.unlock();
		&#125;
	&#125;</code></pre>



<h4 id="锁超时"><a href="#锁超时" class="headerlink" title="锁超时"></a>锁超时</h4><p>使用<strong>lock.tryLock</strong>方法会返回获取锁是否成功。如果成功则返回true，反之则返回false。</p>
<p>并且tryLock方法可以<strong>指定等待时间</strong>，参数为：tryLock(long timeout, TimeUnit unit), 其中timeout为最长等待时间，TimeUnit为时间单位</p>
<p><strong>简而言之</strong>就是：获取失败了、获取超时了或者被打断了，不再阻塞，直接停止运行</p>
<p>不设置等待时间</p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		ReentrantLock lock = <span class="hljs-keyword">new</span> ReentrantLock();
		Thread t1 = <span class="hljs-keyword">new</span> Thread(()-&gt; &#123;
            <span class="hljs-comment">//未设置等待时间，一旦获取失败，直接返回false</span>
			<span class="hljs-keyword">if</span>(!lock.tryLock()) &#123;
				System.out.println(<span class="hljs-string">"获取失败"</span>);
                <span class="hljs-comment">//获取失败，不再向下执行，返回</span>
				<span class="hljs-keyword">return</span>;
			&#125;
			System.out.println(<span class="hljs-string">"得到了锁"</span>);
			lock.unlock();
		&#125;);


		lock.lock();
		<span class="hljs-keyword">try</span>&#123;
			t1.start();
			Thread.sleep(<span class="hljs-number">3000</span>);
		&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
			e.printStackTrace();
		&#125; <span class="hljs-keyword">finally</span> &#123;
			lock.unlock();
		&#125;
	&#125;</code></pre>



<p>设置等待时间</p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		ReentrantLock lock = <span class="hljs-keyword">new</span> ReentrantLock();
		Thread t1 = <span class="hljs-keyword">new</span> Thread(()-&gt; &#123;
			<span class="hljs-keyword">try</span> &#123;
				<span class="hljs-comment">//判断获取锁是否成功，最多等待1秒</span>
				<span class="hljs-keyword">if</span>(!lock.tryLock(<span class="hljs-number">1</span>, TimeUnit.SECONDS)) &#123;
					System.out.println(<span class="hljs-string">"获取失败"</span>);
					<span class="hljs-comment">//获取失败，不再向下执行，直接返回</span>
					<span class="hljs-keyword">return</span>;
				&#125;
			&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
				e.printStackTrace();
				<span class="hljs-comment">//被打断，不再向下执行，直接返回</span>
				<span class="hljs-keyword">return</span>;
			&#125;
			System.out.println(<span class="hljs-string">"得到了锁"</span>);
			<span class="hljs-comment">//释放锁</span>
			lock.unlock();
		&#125;);


		lock.lock();
		<span class="hljs-keyword">try</span>&#123;
			t1.start();
			<span class="hljs-comment">//打断等待</span>
			t1.interrupt();
			Thread.sleep(<span class="hljs-number">3000</span>);
		&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
			e.printStackTrace();
		&#125; <span class="hljs-keyword">finally</span> &#123;
			lock.unlock();
		&#125;
	&#125;</code></pre>



<h4 id="公平锁"><a href="#公平锁" class="headerlink" title="公平锁"></a>公平锁</h4><p>在线程获取锁失败，进入阻塞队列时，<strong>先进入</strong>的会在锁被释放后<strong>先获得</strong>锁。这样的获取方式就是<strong>公平</strong>的。</p>
<pre><code class="hljs java"><span class="hljs-comment">//默认是不公平锁，需要在创建时指定为公平锁</span>
ReentrantLock lock = <span class="hljs-keyword">new</span> ReentrantLock(<span class="hljs-keyword">true</span>);</code></pre>



<h4 id="条件变量"><a href="#条件变量" class="headerlink" title="条件变量"></a>条件变量</h4><p>synchronized 中也有条件变量，就是我们讲原理时那个 waitSet 休息室，当条件不满足时进入waitSet 等待</p>
<p>ReentrantLock 的条件变量比 synchronized 强大之处在于，它是支持<strong>多个</strong>条件变量的，这就好比 </p>
<ul>
<li>synchronized 是那些不满足条件的线程都在一间休息室等消息 </li>
<li>而 ReentrantLock 支持多间休息室，有专门等烟的休息室、专门等早餐的休息室、唤醒时也是按休息室来唤 醒</li>
</ul>
<p>使用要点：</p>
<ul>
<li>await 前需要<strong>获得锁</strong></li>
<li>await 执行后，会释放锁，进入 conditionObject 等待</li>
<li>await 的线程被唤醒（或打断、或超时）取重新竞争 lock 锁 </li>
<li>竞争 lock 锁成功后，从 await 后继续执</li>
</ul>
<pre><code class="hljs java"><span class="hljs-keyword">static</span> Boolean judge = <span class="hljs-keyword">false</span>;
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> InterruptedException </span>&#123;
	ReentrantLock lock = <span class="hljs-keyword">new</span> ReentrantLock();
	<span class="hljs-comment">//获得条件变量</span>
	Condition condition = lock.newCondition();
	<span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
		lock.lock();
		<span class="hljs-keyword">try</span>&#123;
			<span class="hljs-keyword">while</span>(!judge) &#123;
				System.out.println(<span class="hljs-string">"不满足条件，等待..."</span>);
				<span class="hljs-comment">//等待</span>
				condition.await();
			&#125;
		&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
			e.printStackTrace();
		&#125; <span class="hljs-keyword">finally</span> &#123;
			System.out.println(<span class="hljs-string">"执行完毕！"</span>);
			lock.unlock();
		&#125;
	&#125;).start();

	<span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
		lock.lock();
		<span class="hljs-keyword">try</span> &#123;
			Thread.sleep(<span class="hljs-number">1</span>);
			judge = <span class="hljs-keyword">true</span>;
			<span class="hljs-comment">//释放</span>
			condition.signal();
		&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
			e.printStackTrace();
		&#125; <span class="hljs-keyword">finally</span> &#123;
			lock.unlock();
		&#125;

	&#125;).start();
&#125;</code></pre>



<h4 id="通过Lock与AQS实现可重入锁"><a href="#通过Lock与AQS实现可重入锁" class="headerlink" title="通过Lock与AQS实现可重入锁"></a>通过Lock与AQS实现可重入锁</h4><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">MyLock</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Lock</span> </span>&#123;
   <span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Sync</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractQueuedSynchronizer</span> </span>&#123;
      <span class="hljs-meta">@Override</span>
      <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">tryAcquire</span><span class="hljs-params">(<span class="hljs-keyword">int</span> arg)</span> </span>&#123;
         <span class="hljs-keyword">if</span> (getExclusiveOwnerThread() == <span class="hljs-keyword">null</span>) &#123;
            <span class="hljs-keyword">if</span> (compareAndSetState(<span class="hljs-number">0</span>, <span class="hljs-number">1</span>)) &#123;
               setExclusiveOwnerThread(Thread.currentThread());
               <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
            &#125;
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
         &#125;

         <span class="hljs-keyword">if</span> (getExclusiveOwnerThread() == Thread.currentThread()) &#123;
            <span class="hljs-keyword">int</span> state = getState();
            compareAndSetState(state, state + <span class="hljs-number">1</span>);
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
         &#125;

         <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
      &#125;

      <span class="hljs-meta">@Override</span>
      <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">tryRelease</span><span class="hljs-params">(<span class="hljs-keyword">int</span> arg)</span> </span>&#123;
         <span class="hljs-keyword">if</span> (getState() &lt;= <span class="hljs-number">0</span>) &#123;
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalMonitorStateException();
         &#125;

         <span class="hljs-keyword">if</span> (getExclusiveOwnerThread() != Thread.currentThread()) &#123;
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalMonitorStateException();
         &#125;

         <span class="hljs-keyword">int</span> state = getState();
         <span class="hljs-keyword">if</span> (state == <span class="hljs-number">1</span>) &#123;
            setExclusiveOwnerThread(<span class="hljs-keyword">null</span>);
            compareAndSetState(state, <span class="hljs-number">0</span>);
         &#125; <span class="hljs-keyword">else</span> &#123;
            compareAndSetState(state, state - <span class="hljs-number">1</span>);
         &#125;
         <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;
      &#125;

      <span class="hljs-meta">@Override</span>
      <span class="hljs-function"><span class="hljs-keyword">protected</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">isHeldExclusively</span><span class="hljs-params">()</span> </span>&#123;
         <span class="hljs-keyword">return</span> getState() &gt;= <span class="hljs-number">1</span>;
      &#125;

      <span class="hljs-function"><span class="hljs-keyword">public</span> Condition <span class="hljs-title">newCondition</span><span class="hljs-params">()</span> </span>&#123;
         <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ConditionObject();
      &#125;

   &#125;

   Sync sync = <span class="hljs-keyword">new</span> Sync();

   <span class="hljs-meta">@Override</span>
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">lock</span><span class="hljs-params">()</span> </span>&#123;
      sync.acquire(<span class="hljs-number">1</span>);
   &#125;

   <span class="hljs-meta">@Override</span>
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">lockInterruptibly</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> InterruptedException </span>&#123;
      sync.acquireInterruptibly(<span class="hljs-number">1</span>);
   &#125;

   <span class="hljs-meta">@Override</span>
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">tryLock</span><span class="hljs-params">()</span> </span>&#123;
      <span class="hljs-keyword">return</span> sync.tryAcquire(<span class="hljs-number">1</span>);
   &#125;

   <span class="hljs-meta">@Override</span>
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">tryLock</span><span class="hljs-params">(<span class="hljs-keyword">long</span> time, TimeUnit unit)</span> <span class="hljs-keyword">throws</span> InterruptedException </span>&#123;
      <span class="hljs-keyword">return</span> sync.tryAcquireNanos(<span class="hljs-number">1</span>, time);
   &#125;

   <span class="hljs-meta">@Override</span>
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">unlock</span><span class="hljs-params">()</span> </span>&#123;
      sync.release(<span class="hljs-number">1</span>);
   &#125;

   <span class="hljs-meta">@Override</span>
   <span class="hljs-function"><span class="hljs-keyword">public</span> Condition <span class="hljs-title">newCondition</span><span class="hljs-params">()</span> </span>&#123;
      <span class="hljs-keyword">return</span> sync.newCondition();
   &#125;
&#125;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Main</span> </span>&#123;
   <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> num = <span class="hljs-number">0</span>;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> InterruptedException, IOException </span>&#123;
      MyLock lock = <span class="hljs-keyword">new</span> MyLock();

      Object syncLock = <span class="hljs-keyword">new</span> Object();

      Thread t1 = <span class="hljs-keyword">new</span> Thread(() -&gt; &#123;
         <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10000</span>; i++) &#123;
            lock.lock();
            <span class="hljs-keyword">try</span> &#123;
               lock.lock();
               <span class="hljs-keyword">try</span> &#123;
                  lock.lock();
                  <span class="hljs-keyword">try</span> &#123;
                     num++;
                  &#125; <span class="hljs-keyword">finally</span> &#123;
                     lock.unlock();
                  &#125;
               &#125; <span class="hljs-keyword">finally</span> &#123;
                  lock.unlock();
               &#125;
            &#125; <span class="hljs-keyword">finally</span> &#123;
               lock.unlock();
            &#125;
         &#125;
      &#125;);

      Thread t2 = <span class="hljs-keyword">new</span> Thread(() -&gt; &#123;
         <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10000</span>; i++) &#123;
            lock.lock();
            <span class="hljs-keyword">try</span> &#123;
               lock.lock();
               <span class="hljs-keyword">try</span> &#123;
                  lock.lock();
                  <span class="hljs-keyword">try</span> &#123;
                     num--;
                  &#125; <span class="hljs-keyword">finally</span> &#123;
                     lock.unlock();
                  &#125;
               &#125; <span class="hljs-keyword">finally</span> &#123;
                  lock.unlock();
               &#125;
            &#125; <span class="hljs-keyword">finally</span> &#123;
               lock.unlock();
            &#125;
         &#125;
      &#125;);

      t1.start();
      t2.start();
      t1.join();
      t2.join();

      <span class="hljs-keyword">int</span> x = <span class="hljs-number">0</span>;
   &#125;
&#125;</code></pre>



<h2 id="13、同步模式之顺序控制"><a href="#13、同步模式之顺序控制" class="headerlink" title="13、同步模式之顺序控制"></a>13、同步模式之顺序控制</h2><h3 id="Wait-Notify版本"><a href="#Wait-Notify版本" class="headerlink" title="Wait/Notify版本"></a>Wait/Notify版本</h3><pre><code class="hljs java"><span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> Object LOCK = <span class="hljs-keyword">new</span> Object();
<span class="hljs-comment">//判断先执行的内容是否执行完毕</span>
<span class="hljs-keyword">static</span> Boolean judge = <span class="hljs-keyword">false</span>;
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
	<span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
		<span class="hljs-keyword">synchronized</span> (LOCK) &#123;
			<span class="hljs-keyword">while</span> (!judge) &#123;
				<span class="hljs-keyword">try</span> &#123;
					LOCK.wait();
				&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
					e.printStackTrace();
				&#125;
			&#125;
			System.out.println(<span class="hljs-string">"2"</span>);
		&#125;
	&#125;).start();

	<span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
		<span class="hljs-keyword">synchronized</span> (LOCK) &#123;
			System.out.println(<span class="hljs-string">"1"</span>);
			judge = <span class="hljs-keyword">true</span>;
               <span class="hljs-comment">//执行完毕，唤醒所有等待线程</span>
			LOCK.notifyAll();
		&#125;
	&#125;).start();
&#125;</code></pre>





<h3 id="交替输出"><a href="#交替输出" class="headerlink" title="交替输出"></a>交替输出</h3><p><strong>wait/notify版本</strong></p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Test4</span> </span>&#123;
	<span class="hljs-keyword">static</span> Symbol symbol = <span class="hljs-keyword">new</span> Symbol();
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		<span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
			symbol.run(<span class="hljs-string">"a"</span>, <span class="hljs-number">1</span>, <span class="hljs-number">2</span>);
		&#125;).start();

		<span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
			symbol.run(<span class="hljs-string">"b"</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>);

		&#125;).start();
		symbol.run(<span class="hljs-string">"c"</span>, <span class="hljs-number">3</span>, <span class="hljs-number">1</span>);
		<span class="hljs-keyword">new</span> Thread(()-&gt;&#123;

		&#125;).start();
	&#125;
&#125;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Symbol</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">(String str, <span class="hljs-keyword">int</span> flag, <span class="hljs-keyword">int</span> nextFlag)</span> </span>&#123;
		<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i=<span class="hljs-number">0</span>; i&lt;loopNumber; i++) &#123;
			<span class="hljs-keyword">while</span>(flag != <span class="hljs-keyword">this</span>.flag) &#123;
				<span class="hljs-keyword">try</span> &#123;
					<span class="hljs-keyword">this</span>.wait();
				&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
					e.printStackTrace();
				&#125;
			&#125;
			System.out.println(str);
			<span class="hljs-comment">//设置下一个运行的线程标记</span>
			<span class="hljs-keyword">this</span>.flag = nextFlag;
			<span class="hljs-comment">//唤醒所有线程</span>
			<span class="hljs-keyword">this</span>.notifyAll();
		&#125;
	&#125;

	<span class="hljs-comment">/**</span>
<span class="hljs-comment">	 * 线程的执行标记， 1-&gt;a 2-&gt;b 3-&gt;c</span>
<span class="hljs-comment">	 */</span>
	<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> flag = <span class="hljs-number">1</span>;
	<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> loopNumber = <span class="hljs-number">5</span>;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getFlag</span><span class="hljs-params">()</span> </span>&#123;
		<span class="hljs-keyword">return</span> flag;
	&#125;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setFlag</span><span class="hljs-params">(<span class="hljs-keyword">int</span> flag)</span> </span>&#123;
		<span class="hljs-keyword">this</span>.flag = flag;
	&#125;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getLoopNumber</span><span class="hljs-params">()</span> </span>&#123;
		<span class="hljs-keyword">return</span> loopNumber;
	&#125;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setLoopNumber</span><span class="hljs-params">(<span class="hljs-keyword">int</span> loopNumber)</span> </span>&#123;
		<span class="hljs-keyword">this</span>.loopNumber = loopNumber;
	&#125;
&#125;</code></pre>



<p><strong>await/signal版本</strong></p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Test5</span> </span>&#123;
	<span class="hljs-keyword">static</span> AwaitSignal awaitSignal = <span class="hljs-keyword">new</span> AwaitSignal();
	<span class="hljs-keyword">static</span> Condition conditionA = awaitSignal.newCondition();
	<span class="hljs-keyword">static</span> Condition conditionB = awaitSignal.newCondition();
	<span class="hljs-keyword">static</span> Condition conditionC = awaitSignal.newCondition();
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		<span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
			awaitSignal.run(<span class="hljs-string">"a"</span>, conditionA, conditionB);
		&#125;).start();

		<span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
			awaitSignal.run(<span class="hljs-string">"b"</span>, conditionB, conditionC);
		&#125;).start();

		<span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
			awaitSignal.run(<span class="hljs-string">"c"</span>, conditionC, conditionA);
		&#125;).start();


		<span class="hljs-keyword">try</span> &#123;
			Thread.sleep(<span class="hljs-number">1000</span>);
		&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
			e.printStackTrace();
		&#125;
		awaitSignal.lock();
		<span class="hljs-keyword">try</span> &#123;
            <span class="hljs-comment">//唤醒一个等待的线程</span>
			conditionA.signal();
		&#125;<span class="hljs-keyword">finally</span> &#123;
			awaitSignal.unlock();
		&#125;
	&#125;
&#125;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AwaitSignal</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">ReentrantLock</span></span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">(String str, Condition thisCondition, Condition nextCondition)</span> </span>&#123;
		<span class="hljs-keyword">for</span>(<span class="hljs-keyword">int</span> i=<span class="hljs-number">0</span>; i&lt;loopNumber; i++) &#123;
			lock();
			<span class="hljs-keyword">try</span> &#123;
                <span class="hljs-comment">//全部进入等待状态</span>
				thisCondition.await();
				System.out.print(str);
				nextCondition.signal();
			&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
				e.printStackTrace();
			&#125; <span class="hljs-keyword">finally</span> &#123;
				unlock();
			&#125;
		&#125;
	&#125;

	<span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> loopNumber=<span class="hljs-number">5</span>;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getLoopNumber</span><span class="hljs-params">()</span> </span>&#123;
		<span class="hljs-keyword">return</span> loopNumber;
	&#125;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">setLoopNumber</span><span class="hljs-params">(<span class="hljs-keyword">int</span> loopNumber)</span> </span>&#123;
		<span class="hljs-keyword">this</span>.loopNumber = loopNumber;
	&#125;
&#125;</code></pre>



<h2 id="14、ThreadLocal"><a href="#14、ThreadLocal" class="headerlink" title="14、ThreadLocal"></a>14、ThreadLocal</h2><h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><p>ThreadLocal是JDK包提供的，它提供了线程本地变量，也就是如果你创建了一个ThreadLocal变量，那么<strong>访问这个变量的每个线程都会有这个变量的一个本地副本</strong>。当多个线程操作这个变量时，实际操作的是自己本地内存里面的变量，从而避免了线程安全问题</p>
<h3 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h3><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ThreadLocalStudy</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      <span class="hljs-comment">// 创建ThreadLocal变量</span>
      ThreadLocal&lt;String&gt; stringThreadLocal = <span class="hljs-keyword">new</span> ThreadLocal&lt;&gt;();
      ThreadLocal&lt;User&gt; userThreadLocal = <span class="hljs-keyword">new</span> ThreadLocal&lt;&gt;();

      <span class="hljs-comment">// 创建两个线程，分别使用上面的两个ThreadLocal变量</span>
      Thread thread1 = <span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
         <span class="hljs-comment">// stringThreadLocal第一次赋值</span>
         stringThreadLocal.set(<span class="hljs-string">"thread1 stringThreadLocal first"</span>);
         <span class="hljs-comment">// stringThreadLocal第二次赋值</span>
         stringThreadLocal.set(<span class="hljs-string">"thread1 stringThreadLocal second"</span>);
         <span class="hljs-comment">// userThreadLocal赋值</span>
         userThreadLocal.set(<span class="hljs-keyword">new</span> User(<span class="hljs-string">"Nyima"</span>, <span class="hljs-number">20</span>));

         <span class="hljs-comment">// 取值</span>
         System.out.println(stringThreadLocal.get());
         System.out.println(userThreadLocal.get());
          
          <span class="hljs-comment">// 移除</span>
		 userThreadLocal.remove();
		 System.out.println(userThreadLocal.get());
      &#125;);

      Thread thread2 = <span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
         <span class="hljs-comment">// stringThreadLocal第一次赋值</span>
         stringThreadLocal.set(<span class="hljs-string">"thread2 stringThreadLocal first"</span>);
         <span class="hljs-comment">// stringThreadLocal第二次赋值</span>
         stringThreadLocal.set(<span class="hljs-string">"thread2 stringThreadLocal second"</span>);
         <span class="hljs-comment">// userThreadLocal赋值</span>
         userThreadLocal.set(<span class="hljs-keyword">new</span> User(<span class="hljs-string">"Hulu"</span>, <span class="hljs-number">20</span>));

         <span class="hljs-comment">// 取值</span>
         System.out.println(stringThreadLocal.get());
         System.out.println(userThreadLocal.get());
      &#125;);

      <span class="hljs-comment">// 启动线程</span>
      thread1.start();
      thread2.start();
   &#125;
&#125;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">User</span> </span>&#123;
   String name;
   <span class="hljs-keyword">int</span> age;

   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">User</span><span class="hljs-params">(String name, <span class="hljs-keyword">int</span> age)</span> </span>&#123;
      <span class="hljs-keyword">this</span>.name = name;
      <span class="hljs-keyword">this</span>.age = age;
   &#125;

   <span class="hljs-meta">@Override</span>
   <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>&#123;
      <span class="hljs-keyword">return</span> <span class="hljs-string">"User&#123;"</span> +
            <span class="hljs-string">"name='"</span> + name + <span class="hljs-string">'\''</span> +
            <span class="hljs-string">", age="</span> + age +
            <span class="hljs-string">'&#125;'</span>;
   &#125;
&#125;</code></pre>

<p><strong>运行结果</strong></p>
<pre><code class="hljs java">thread1 stringThreadLocal second
thread2 stringThreadLocal second
User&#123;name=<span class="hljs-string">'Nyima'</span>, age=<span class="hljs-number">20</span>&#125;
User&#123;name=<span class="hljs-string">'Hulu'</span>, age=<span class="hljs-number">20</span>&#125;
<span class="hljs-keyword">null</span></code></pre>

<p>从运行结果可以看出</p>
<ul>
<li>每个线程中的ThreadLocal变量是每个线程私有的，而不是共享的<ul>
<li>从线程1和线程2的打印结果可以看出</li>
</ul>
</li>
<li>ThreadLocal其实就相当于其泛型类型的一个变量，只不过是每个线程私有的<ul>
<li>stringThreadLocal被赋值了两次，保存的是最后一次赋值的结果</li>
</ul>
</li>
<li>ThreadLocal可以进行以下几个操作<ul>
<li>set 设置值</li>
<li>get 取出值</li>
<li>remove 移除值</li>
</ul>
</li>
</ul>
<h3 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h3><h4 id="Thread中的threadLocals"><a href="#Thread中的threadLocals" class="headerlink" title="Thread中的threadLocals"></a>Thread中的threadLocals</h4><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Thread</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Runnable</span> </span>&#123;
 ...

 ThreadLocal.ThreadLocalMap threadLocals = <span class="hljs-keyword">null</span>;

 <span class="hljs-comment">// 放在后面说</span>
 ThreadLocal.ThreadLocalMap inheritableThreadLocals = <span class="hljs-keyword">null</span>;

 ...
&#125;</code></pre>

<pre><code class="hljs java"><span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ThreadLocalMap</span> </span>&#123;
    <span class="hljs-keyword">static</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Entry</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">WeakReference</span>&lt;<span class="hljs-title">ThreadLocal</span>&lt;?&gt;&gt; </span>&#123;
        <span class="hljs-comment">/** The value associated with this ThreadLocal. */</span>
        Object value;

        Entry(ThreadLocal&lt;?&gt; k, Object v) &#123;
            <span class="hljs-keyword">super</span>(k);
            value = v;
        &#125;
    &#125;</code></pre>

<p>可以看出Thread类中有一个threadLocals和一个inheritableThreadLocals，它们都是ThreadLocalMap类型的变量，而ThreadLocalMap是一个定制化的Hashmap。在默认情况下，每个线程中的这两个变量都为null。此处先讨论threadLocals，inheritableThreadLocals放在后面讨论</p>
<h4 id="ThreadLocal中的方法"><a href="#ThreadLocal中的方法" class="headerlink" title="ThreadLocal中的方法"></a><strong>ThreadLocal中的方法</strong></h4><p><strong>set方法</strong></p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">set</span><span class="hljs-params">(T value)</span> </span>&#123;
    <span class="hljs-comment">// 获取当前线程</span>
    Thread t = Thread.currentThread();
    
    <span class="hljs-comment">// 获得ThreadLocalMap对象 </span>
    <span class="hljs-comment">// 这里的get会返回Thread类中的threadLocals</span>
    ThreadLocalMap map = getMap(t);
    
    <span class="hljs-comment">// 判断map是否已经创建，没创建就创建并放入值，创建了就直接放入</span>
    <span class="hljs-keyword">if</span> (map != <span class="hljs-keyword">null</span>)
        <span class="hljs-comment">// ThreadLocal自生的引用作为key，传入的值作为value</span>
        map.set(<span class="hljs-keyword">this</span>, value);
    <span class="hljs-keyword">else</span>
        createMap(t, value);
&#125;</code></pre>

<p><strong>如果未创建</strong></p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">createMap</span><span class="hljs-params">(Thread t, T firstValue)</span> </span>&#123;
    <span class="hljs-comment">// 创建的同时设置想放入的值</span>
    <span class="hljs-comment">// hreadLocal自生的引用作为key，传入的值作为value</span>
    t.threadLocals = <span class="hljs-keyword">new</span> ThreadLocalMap(<span class="hljs-keyword">this</span>, firstValue);
&#125;</code></pre>



<p><strong>get方法</strong></p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> T <span class="hljs-title">get</span><span class="hljs-params">()</span> </span>&#123;
    <span class="hljs-comment">// 获取当前线程</span>
    Thread t = Thread.currentThread();
	<span class="hljs-comment">// 获取当前线程的threadLocals变量</span>
    ThreadLocalMap map = getMap(t);
    
    <span class="hljs-comment">// 判断threadLocals是否被初始化了</span>
    <span class="hljs-keyword">if</span> (map != <span class="hljs-keyword">null</span>) &#123;
        <span class="hljs-comment">// 已经初始化则直接返回</span>
        ThreadLocalMap.Entry e = map.getEntry(<span class="hljs-keyword">this</span>);
        <span class="hljs-keyword">if</span> (e != <span class="hljs-keyword">null</span>) &#123;
            <span class="hljs-meta">@SuppressWarnings</span>(<span class="hljs-string">"unchecked"</span>)
            T result = (T)e.value;
            <span class="hljs-keyword">return</span> result;
        &#125;
    &#125;
    <span class="hljs-comment">// 否则就创建threadLocals</span>
    <span class="hljs-keyword">return</span> setInitialValue();
&#125;</code></pre>

<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">private</span> T <span class="hljs-title">setInitialValue</span><span class="hljs-params">()</span> </span>&#123;
    <span class="hljs-comment">// 这个方法返回是null</span>
    T value = initialValue();
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    
    <span class="hljs-comment">// 无论map创建与否，最终value的值都为null</span>
    <span class="hljs-keyword">if</span> (map != <span class="hljs-keyword">null</span>)
        map.set(<span class="hljs-keyword">this</span>, value);
    <span class="hljs-keyword">else</span>
        createMap(t, value);
    <span class="hljs-keyword">return</span> value;
&#125;</code></pre>

<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">protected</span> T <span class="hljs-title">initialValue</span><span class="hljs-params">()</span> </span>&#123;
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
&#125;</code></pre>



<p><strong>remove方法</strong></p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">remove</span><span class="hljs-params">()</span> </span>&#123;
    ThreadLocalMap m = getMap(Thread.currentThread());
    <span class="hljs-keyword">if</span> (m != <span class="hljs-keyword">null</span>)
        <span class="hljs-comment">// 如果threadLocals已经被初始化，则移除</span>
        m.remove(<span class="hljs-keyword">this</span>);
&#125;</code></pre>



<h4 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a><strong>总结</strong></h4><p>在每个线程内部都有一个名为threadLocals的成员变量，该变量的类型为HashMap，其中<strong>key为我们定义的ThreadLocal变量的this引用，value则为我们使用set方法设置的值</strong>。每个线程的本地变量存放在线程自己的内存变量threadLocals中</p>
<p>只有当前线程<strong>第一次调用ThreadLocal的set或者get方法时才会创建threadLocals</strong>（inheritableThreadLocals也是一样）。其实每个线程的本地变量不是存放在ThreadLocal实例里面，而是存放在调用线程的threadLocals变量里面</p>
<h2 id="15、InheritableThreadLocal"><a href="#15、InheritableThreadLocal" class="headerlink" title="15、InheritableThreadLocal"></a>15、InheritableThreadLocal</h2><h3 id="简介-1"><a href="#简介-1" class="headerlink" title="简介"></a>简介</h3><p>从ThreadLocal的源码可以看出，无论是set、get、还是remove，都是相对于当前线程操作的</p>
<pre><code class="hljs java">Thread.currentThread()</code></pre>

<p>所以ThreadLocal无法从父线程传向子线程，所以InheritableThreadLocal出现了，<strong>它能够让父线程中ThreadLocal的值传给子线程。</strong></p>
<p>也就是从main所在的线程，传给thread1或thread2</p>
<h3 id="使用-1"><a href="#使用-1" class="headerlink" title="使用"></a>使用</h3><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo1</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      ThreadLocal&lt;String&gt; stringThreadLocal = <span class="hljs-keyword">new</span> ThreadLocal&lt;&gt;();
      InheritableThreadLocal&lt;String&gt; stringInheritable = <span class="hljs-keyword">new</span> InheritableThreadLocal&lt;&gt;();

      <span class="hljs-comment">// 主线程赋对上面两个变量进行赋值</span>
      stringThreadLocal.set(<span class="hljs-string">"this is threadLocal"</span>);
      stringInheritable.set(<span class="hljs-string">"this is inheritableThreadLocal"</span>);

      <span class="hljs-comment">// 创建线程</span>
      Thread thread1 = <span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
         <span class="hljs-comment">// 获得ThreadLocal中存放的值</span>
         System.out.println(stringThreadLocal.get());

         <span class="hljs-comment">// 获得InheritableThreadLocal存放的值</span>
         System.out.println(stringInheritable.get());
      &#125;);

      thread1.start();
   &#125;
&#125;</code></pre>

<p><strong>运行结果</strong></p>
<pre><code class="hljs java"><span class="hljs-keyword">null</span>
<span class="hljs-keyword">this</span> is inheritableThreadLocal</code></pre>

<p>可以看出InheritableThreadLocal的值成功从主线程传入了子线程，而ThreadLocal则没有</p>
<h3 id="原理-1"><a href="#原理-1" class="headerlink" title="原理"></a>原理</h3><h4 id="InheritableThreadLocal"><a href="#InheritableThreadLocal" class="headerlink" title="InheritableThreadLocal"></a>InheritableThreadLocal</h4><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">InheritableThreadLocal</span>&lt;<span class="hljs-title">T</span>&gt; <span class="hljs-keyword">extends</span> <span class="hljs-title">ThreadLocal</span>&lt;<span class="hljs-title">T</span>&gt; </span>&#123;
    <span class="hljs-comment">// 传入父线程中的一个值，然后直接返回</span>
    <span class="hljs-function"><span class="hljs-keyword">protected</span> T <span class="hljs-title">childValue</span><span class="hljs-params">(T parentValue)</span> </span>&#123;
        <span class="hljs-keyword">return</span> parentValue;
    &#125;

  	<span class="hljs-comment">// 返回传入线程的inheritableThreadLocals</span>
    <span class="hljs-comment">// Thread中有一个inheritableThreadLocals变量</span>
    <span class="hljs-comment">// ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;</span>
    <span class="hljs-function">ThreadLocalMap <span class="hljs-title">getMap</span><span class="hljs-params">(Thread t)</span> </span>&#123;
       <span class="hljs-keyword">return</span> t.inheritableThreadLocals;
    &#125;

 	<span class="hljs-comment">// 创建一个inheritableThreadLocals</span>
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">createMap</span><span class="hljs-params">(Thread t, T firstValue)</span> </span>&#123;
        t.inheritableThreadLocals = <span class="hljs-keyword">new</span> ThreadLocalMap(<span class="hljs-keyword">this</span>, firstValue);
    &#125;
&#125;</code></pre>

<p>由如上代码可知，InheritableThreadLocal继承了ThreadLocal，并重写了三个方法。InheritableThreadLocal重写了<strong>createMap方法</strong>，那么现在当第一次调用set方法时，创建的是当前线程的inheritableThreadLocals变量的实例而不再是threadLocals。当调用<strong>getMap方法</strong>获取当前线程内部的map变量时，获取的是inheritableThreadLocals而不再是threadLocals</p>
<h4 id="childValue-T-parentValue-方法的调用"><a href="#childValue-T-parentValue-方法的调用" class="headerlink" title="childValue(T parentValue)方法的调用"></a>childValue(T parentValue)方法的调用</h4><p>在主函数运行时，会调用Thread的默认构造函数（<strong>创建主线程</strong>，也就是父线程），所以我们先看看Thread的默认构造函数</p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Thread</span><span class="hljs-params">()</span> </span>&#123;
    init(<span class="hljs-keyword">null</span>, <span class="hljs-keyword">null</span>, <span class="hljs-string">"Thread-"</span> + nextThreadNum(), <span class="hljs-number">0</span>);
&#125;</code></pre>

<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">init</span><span class="hljs-params">(ThreadGroup g, Runnable target, String name,</span></span>
<span class="hljs-function"><span class="hljs-params">                  <span class="hljs-keyword">long</span> stackSize, AccessControlContext acc,</span></span>
<span class="hljs-function"><span class="hljs-params">                  <span class="hljs-keyword">boolean</span> inheritThreadLocals)</span> </span>&#123;
   	...
        
	<span class="hljs-comment">// 获得当前线程的，在这里是主线程</span>
    Thread parent = currentThread();
   
    ...
    
    <span class="hljs-comment">// 如果父线程的inheritableThreadLocals存在</span>
    <span class="hljs-comment">// 我们在主线程中调用set和get时，会创建inheritableThreadLocals</span>
    <span class="hljs-keyword">if</span> (inheritThreadLocals &amp;&amp; parent.inheritableThreadLocals != <span class="hljs-keyword">null</span>)
        <span class="hljs-comment">// 设置子线程的inheritableThreadLocals</span>
        <span class="hljs-keyword">this</span>.inheritableThreadLocals =
            ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
    
    <span class="hljs-comment">/* Stash the specified stack size in case the VM cares */</span>
    <span class="hljs-keyword">this</span>.stackSize = stackSize;

    <span class="hljs-comment">/* Set thread ID */</span>
    tid = nextThreadID();
&#125;</code></pre>

<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">static</span> ThreadLocalMap <span class="hljs-title">createInheritedMap</span><span class="hljs-params">(ThreadLocalMap parentMap)</span> </span>&#123;
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ThreadLocalMap(parentMap);
&#125;</code></pre>

<p>在createInheritedMap内部使用父线程的inheritableThreadLocals变量作为构造函数创建了一个新的ThreadLocalMap变量，然后赋值给了子线程的inheritableThreadLocals变量</p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-title">ThreadLocalMap</span><span class="hljs-params">(ThreadLocalMap parentMap)</span> </span>&#123;
    Entry[] parentTable = parentMap.table;
    <span class="hljs-keyword">int</span> len = parentTable.length;
    setThreshold(len);
    table = <span class="hljs-keyword">new</span> Entry[len];

    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> j = <span class="hljs-number">0</span>; j &lt; len; j++) &#123;
        Entry e = parentTable[j];
        <span class="hljs-keyword">if</span> (e != <span class="hljs-keyword">null</span>) &#123;
            <span class="hljs-meta">@SuppressWarnings</span>(<span class="hljs-string">"unchecked"</span>)
            ThreadLocal&lt;Object&gt; key = (ThreadLocal&lt;Object&gt;) e.get();
            <span class="hljs-keyword">if</span> (key != <span class="hljs-keyword">null</span>) &#123;
                <span class="hljs-comment">// 这里调用了 childValue 方法</span>
                <span class="hljs-comment">// 该方法会返回parent的值</span>
                Object value = key.childValue(e.value);
                
                Entry c = <span class="hljs-keyword">new</span> Entry(key, value);
                <span class="hljs-keyword">int</span> h = key.threadLocalHashCode &amp; (len - <span class="hljs-number">1</span>);
                <span class="hljs-keyword">while</span> (table[h] != <span class="hljs-keyword">null</span>)
                    h = nextIndex(h, len);
                table[h] = c;
                size++;
            &#125;
        &#125;
    &#125;
&#125;</code></pre>

<p>在该构造函数内部把父线程的inheritableThreadLocals成员变量的值复制到新的ThreadLocalMap对象中</p>
<h4 id="总结-2"><a href="#总结-2" class="headerlink" title="总结"></a>总结</h4><p>InheritableThreadLocal类通过重写getMap和createMap，让本地变量保存到了具体线程的inheritableThreadLocals变量里面，那么线程在通过InheritableThreadLocal类实例的set或者get方法设置变量时，就会创建当前线程的inheritableThreadLocals变量。</p>
<p><strong>当父线程创建子线程时，构造函数会把父线程中inheritableThreadLocals变量里面的本地变量复制一份保存到子线程的inheritableThreadLocals变量里面。</strong></p>
<h1 id="四、共享模型之内存"><a href="#四、共享模型之内存" class="headerlink" title="四、共享模型之内存"></a>四、共享模型之内存</h1><h2 id="1、JAVA内存模型（JMM）"><a href="#1、JAVA内存模型（JMM）" class="headerlink" title="1、JAVA内存模型（JMM）"></a>1、JAVA内存模型（JMM）</h2><p>JMM 即 Java Memory Model，它定义了<strong>主存（共享内存）、工作内存（线程私有）</strong>抽象概念，底层对应着 CPU 寄存器、缓存、硬件内存、 CPU 指令优化等。 </p>
<p><strong>JMM体现在以下几个方面</strong></p>
<ul>
<li>原子性 - 保证指令不会受到线程上下文切换的影响</li>
<li>可见性 - 保证指令不会受 cpu 缓存的影响</li>
<li>有序性 - 保证指令不会受 cpu 指令并行优化的影响</li>
</ul>
<h2 id="2、可见性"><a href="#2、可见性" class="headerlink" title="2、可见性"></a>2、可见性</h2><h4 id="引例"><a href="#引例" class="headerlink" title="引例"></a>引例</h4><p><strong>退出不出的循环</strong></p>
<pre><code class="hljs java"><span class="hljs-keyword">static</span> Boolean run = <span class="hljs-keyword">true</span>;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> InterruptedException </span>&#123;
		<span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
			<span class="hljs-keyword">while</span> (run) &#123;
				<span class="hljs-comment">//如果run为真，则一直执行</span>
			&#125;
		&#125;).start();

		Thread.sleep(<span class="hljs-number">1000</span>);
		System.out.println(<span class="hljs-string">"改变run的值为false"</span>);
		run = <span class="hljs-keyword">false</span>;
	&#125;</code></pre>



<p><strong>为什么无法退出该循环</strong></p>
<ul>
<li>初始状态， t 线程刚开始从<strong>主内存</strong>读取了 run 的值到<strong>工作内存</strong>。</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145505.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li>因为 t 线程要频繁从主内存中读取 run 的值，JIT 编译器会将 run 的值<strong>缓存至自己工作内存</strong>中的高速缓存中， 减少对主存中 run 的访问，提高效率</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145517.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li>1 秒之后，main 线程修改了 run 的值，并同步至主存，而 t 是从自己工作内存中的高速缓存中读取这个变量 的值，结果永远是<strong>旧值</strong></li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145529.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>解决方法</strong></p>
<ul>
<li>使用<strong>volatile</strong>易变关键字</li>
<li>它可以用来修饰<strong>成员变量</strong>和<strong>静态成员变量</strong>（放在主存中的变量），他可以避免线程从自己的工作缓存中查找变量的值，必须到主存中获取它的值，线程操作 volatile 变量都是<strong>直接操作主存</strong> </li>
</ul>
<pre><code class="hljs java"><span class="hljs-comment">//使用易变关键字</span>
<span class="hljs-keyword">volatile</span> <span class="hljs-keyword">static</span> Boolean run = <span class="hljs-keyword">true</span>;
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> InterruptedException </span>&#123;
	<span class="hljs-keyword">new</span> Thread(()-&gt;&#123;
		<span class="hljs-keyword">while</span> (run) &#123;
			<span class="hljs-comment">//如果run为真，则一直执行</span>
		&#125;
	&#125;).start();

	Thread.sleep(<span class="hljs-number">1000</span>);
	System.out.println(<span class="hljs-string">"改变run的值为false"</span>);
	run = <span class="hljs-keyword">false</span>;
&#125;</code></pre>



<h4 id="可见性与原子性"><a href="#可见性与原子性" class="headerlink" title="可见性与原子性"></a>可见性与原子性</h4><p>前面例子体现的实际就是<strong>可见性</strong>，它保证的是在多个线程之间，一个线程对<strong>volatile变量</strong>的修改对另一个线程可见， <strong>不能</strong>保证原子性，仅用在<strong>一个写</strong>线程，<strong>多个读</strong>线程的情况</p>
<ul>
<li><p>注意 synchronized 语句块既可以保证代码块的<strong>原子性</strong>，也同时保证代码块内变量的<strong>可见性</strong>。</p>
</li>
<li><p>但缺点是 synchronized 是属于<strong>重量级</strong>操作，性能相对更低。</p>
</li>
<li><p>如果在前面示例的死循环中加入 System.out.println() 会发现即使不加 volatile 修饰符，线程 t 也能正确看到 对 run 变量的修改了，想一想为什么？</p>
<ul>
<li><p>因为使用了<strong>synchronized</strong>关键字</p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">println</span><span class="hljs-params">(String x)</span> </span>&#123;
		<span class="hljs-comment">//使用了synchronized关键字</span>
        <span class="hljs-keyword">synchronized</span> (<span class="hljs-keyword">this</span>) &#123;
            print(x);
            newLine();
        &#125;
    &#125;</code></pre>



</li>
</ul>
</li>
</ul>
<h4 id="两阶终止模式优化"><a href="#两阶终止模式优化" class="headerlink" title="两阶终止模式优化"></a>两阶终止模式优化</h4><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Test7</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> InterruptedException </span>&#123;
		Monitor monitor = <span class="hljs-keyword">new</span> Monitor();
		monitor.start();
		Thread.sleep(<span class="hljs-number">3500</span>);
		monitor.stop();
	&#125;
&#125;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Monitor</span> </span>&#123;

	Thread monitor;
	<span class="hljs-comment">//设置标记，用于判断是否被终止了</span>
	<span class="hljs-keyword">private</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">boolean</span> stop = <span class="hljs-keyword">false</span>;
	<span class="hljs-comment">/**</span>
<span class="hljs-comment">	 * 启动监控器线程</span>
<span class="hljs-comment">	 */</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">start</span><span class="hljs-params">()</span> </span>&#123;
		<span class="hljs-comment">//设置线控器线程，用于监控线程状态</span>
		monitor = <span class="hljs-keyword">new</span> Thread() &#123;
			<span class="hljs-meta">@Override</span>
			<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>&#123;
				<span class="hljs-comment">//开始不停的监控</span>
				<span class="hljs-keyword">while</span> (<span class="hljs-keyword">true</span>) &#123;
					<span class="hljs-keyword">if</span>(stop) &#123;
						System.out.println(<span class="hljs-string">"处理后续任务"</span>);
						<span class="hljs-keyword">break</span>;
					&#125;
					System.out.println(<span class="hljs-string">"监控器运行中..."</span>);
					<span class="hljs-keyword">try</span> &#123;
						<span class="hljs-comment">//线程休眠</span>
						Thread.sleep(<span class="hljs-number">1000</span>);
					&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
						System.out.println(<span class="hljs-string">"被打断了"</span>);
					&#125;
				&#125;
			&#125;
		&#125;;
		monitor.start();
	&#125;

	<span class="hljs-comment">/**</span>
<span class="hljs-comment">	 * 	用于停止监控器线程</span>
<span class="hljs-comment">	 */</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">stop</span><span class="hljs-params">()</span> </span>&#123;
		<span class="hljs-comment">//打断线程</span>
		monitor.interrupt();
        <span class="hljs-comment">//修改标记</span>
		stop = <span class="hljs-keyword">true</span>;
	&#125;
&#125;</code></pre>



<h4 id="同步模式之犹豫模式"><a href="#同步模式之犹豫模式" class="headerlink" title="同步模式之犹豫模式"></a>同步模式之犹豫模式</h4><p><strong>定义</strong> </p>
<p>Balking （犹豫）模式用在一个线程发现另一个线程或本线程<strong>已经做了某一件相同</strong>的事，那么本线程就无需再做 了，<strong>直接结束返回</strong> </p>
<ul>
<li>用一个标记来判断该任务是否已经被执行过了</li>
<li>需要避免线程安全问题<ul>
<li>加锁的代码块要尽量的小，以保证性能</li>
</ul>
</li>
</ul>
<pre><code class="hljs java"><span class="hljs-keyword">package</span> com.nyima.day1;

<span class="hljs-comment">/**</span>
<span class="hljs-comment"> * <span class="hljs-doctag">@author</span> Chen Panwen</span>
<span class="hljs-comment"> * <span class="hljs-doctag">@data</span> 2020/3/26 16:11</span>
<span class="hljs-comment"> */</span>
<span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Test7</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> InterruptedException </span>&#123;
		Monitor monitor = <span class="hljs-keyword">new</span> Monitor();
		monitor.start();
		monitor.start();
		Thread.sleep(<span class="hljs-number">3500</span>);
		monitor.stop();
	&#125;
&#125;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Monitor</span> </span>&#123;

	Thread monitor;
	<span class="hljs-comment">//设置标记，用于判断是否被终止了</span>
	<span class="hljs-keyword">private</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">boolean</span> stop = <span class="hljs-keyword">false</span>;
	<span class="hljs-comment">//设置标记，用于判断是否已经启动过了</span>
	<span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> starting = <span class="hljs-keyword">false</span>;
	<span class="hljs-comment">/**</span>
<span class="hljs-comment">	 * 启动监控器线程</span>
<span class="hljs-comment">	 */</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">start</span><span class="hljs-params">()</span> </span>&#123;
		<span class="hljs-comment">//上锁，避免多线程运行时出现线程安全问题</span>
		<span class="hljs-keyword">synchronized</span> (<span class="hljs-keyword">this</span>) &#123;
			<span class="hljs-keyword">if</span> (starting) &#123;
				<span class="hljs-comment">//已被启动，直接返回</span>
				<span class="hljs-keyword">return</span>;
			&#125;
			<span class="hljs-comment">//启动监视器，改变标记</span>
			starting = <span class="hljs-keyword">true</span>;
		&#125;
		<span class="hljs-comment">//设置线控器线程，用于监控线程状态</span>
		monitor = <span class="hljs-keyword">new</span> Thread() &#123;
			<span class="hljs-meta">@Override</span>
			<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>&#123;
				<span class="hljs-comment">//开始不停的监控</span>
				<span class="hljs-keyword">while</span> (<span class="hljs-keyword">true</span>) &#123;
					<span class="hljs-keyword">if</span>(stop) &#123;
						System.out.println(<span class="hljs-string">"处理后续任务"</span>);
						<span class="hljs-keyword">break</span>;
					&#125;
					System.out.println(<span class="hljs-string">"监控器运行中..."</span>);
					<span class="hljs-keyword">try</span> &#123;
						<span class="hljs-comment">//线程休眠</span>
						Thread.sleep(<span class="hljs-number">1000</span>);
					&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
						System.out.println(<span class="hljs-string">"被打断了"</span>);
					&#125;
				&#125;
			&#125;
		&#125;;
		monitor.start();
	&#125;

	<span class="hljs-comment">/**</span>
<span class="hljs-comment">	 * 	用于停止监控器线程</span>
<span class="hljs-comment">	 */</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">stop</span><span class="hljs-params">()</span> </span>&#123;
		<span class="hljs-comment">//打断线程</span>
		monitor.interrupt();
		stop = <span class="hljs-keyword">true</span>;
	&#125;
&#125;</code></pre>



<h2 id="3、有序性"><a href="#3、有序性" class="headerlink" title="3、有序性"></a>3、有序性</h2><h3 id="指令重排"><a href="#指令重排" class="headerlink" title="指令重排"></a>指令重排</h3><ul>
<li>JVM 会在<strong>不影响正确性</strong>的前提下，可以<strong>调整</strong>语句的执行<strong>顺序</strong></li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145546.png" srcset="/img/loading.gif" alt=""></p>
<p>这种特性称之为『<strong>指令重排</strong>』，<strong>多线程下『指令重排』会影响正确性</strong>。</p>
<h3 id="指令重排序优化"><a href="#指令重排序优化" class="headerlink" title="指令重排序优化"></a>指令重排序优化</h3><ul>
<li>事实上，现代处理器会设计为一个时钟周期完成一条执行时间长的 CPU 指令。为什么这么做呢？可以想到指令还可以再划分成一个个更小的阶段，例如，每条指令都可以分为： <strong>取指令 - 指令译码 - 执行指令 - 内存访问 - 数据写回</strong> 这5 个阶段</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145615.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li><p>在不改变程序结果的前提下，这些指令的各个阶段可以通过<strong>重排序</strong>和<strong>组合</strong>来实现<strong>指令级并行</strong></p>
</li>
<li><p>指令重排的前提是，重排指令<strong>不能影响结果</strong>，例如</p>
<pre><code class="hljs java"><span class="hljs-comment">// 可以重排的例子 </span>
<span class="hljs-keyword">int</span> a = <span class="hljs-number">10</span>; 
<span class="hljs-keyword">int</span> b = <span class="hljs-number">20</span>; 
System.out.println( a + b );

<span class="hljs-comment">// 不能重排的例子 </span>
<span class="hljs-keyword">int</span> a = <span class="hljs-number">10</span>;
<span class="hljs-keyword">int</span> b = a - <span class="hljs-number">5</span>;</code></pre>



</li>
</ul>
<h3 id="支持流水线的处理器"><a href="#支持流水线的处理器" class="headerlink" title="支持流水线的处理器"></a>支持流水线的处理器</h3><p>现代 CPU 支持多级<strong>指令流水线</strong>，例如支持<strong>同时</strong>执行 <strong>取指令 - 指令译码 - 执行指令 - 内存访问 - 数据写回</strong> 的处理器，就可以称之为五级指令流水线。这时 CPU 可以在一个时钟周期内，同时运行五条指令的不同阶段（相当于一 条执行时间长的复杂指令），IPC = 1，本质上，流水线技术并不能缩短单条指令的执行时间，但它变相地提高了指令地<strong>吞吐率</strong>。</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145602.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>在多线程环境下，指令重排序可能导致出现意料之外的结果</strong></p>
<h3 id="解决办法"><a href="#解决办法" class="headerlink" title="解决办法"></a>解决办法</h3><p><strong>volatile</strong> 修饰的变量，可以<strong>禁用</strong>指令重排</p>
<ul>
<li>禁止的是加volatile关键字变量之前的代码被重排序</li>
</ul>
<h2 id="4、内存屏障"><a href="#4、内存屏障" class="headerlink" title="4、内存屏障"></a>4、内存屏障</h2><ul>
<li>可见性 <ul>
<li><strong>写屏障</strong>（sfence）保证在该屏障<strong>之前</strong>的，对共享变量的改动，都同步到主存当中 </li>
<li><strong>读屏障</strong>（lfence）保证在该屏障<strong>之后</strong>，对共享变量的读取，加载的是主存中新数据 </li>
</ul>
</li>
<li>有序性 <ul>
<li>写屏障会确保指令重排序时，不会将<strong>写屏障之前</strong>的代码排在写屏障之后 </li>
<li>读屏障会确保指令重排序时，不会将<strong>读屏障之后</strong>的代码排在读屏障之前</li>
</ul>
</li>
</ul>
<h2 id="5、volatile-原理"><a href="#5、volatile-原理" class="headerlink" title="5、volatile 原理"></a>5、volatile 原理</h2><p>volatile的底层实现原理是<strong>内存屏障</strong>，Memory Barrier（Memory Fence）</p>
<ul>
<li>对 volatile 变量的写指令后会加入写屏障 </li>
<li>对 volatile 变量的读指令前会加入读屏障</li>
</ul>
<h3 id="如何保证可见性"><a href="#如何保证可见性" class="headerlink" title="如何保证可见性"></a>如何保证可见性</h3><ul>
<li><p>写屏障（sfence）保证在该屏障之前的，对共享变量的改动，都同步到主存当中</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145630.png" srcset="/img/loading.gif" alt=""></p>
</li>
<li><p>而读屏障（lfence）保证在该屏障之后，对共享变量的读取，加载的是主存中新数据</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145713.png" srcset="/img/loading.gif" alt=""></p>
</li>
</ul>
<h3 id="如何保证有序性"><a href="#如何保证有序性" class="headerlink" title="如何保证有序性"></a>如何保证有序性</h3><ul>
<li><p>写屏障会确保指令重排序时，不会将写屏障之前的代码排在写屏障之后</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145723.png" srcset="/img/loading.gif" alt=""></p>
</li>
<li><p>读屏障会确保指令重排序时，不会将读屏障之后的代码排在读屏障之前</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145729.png" srcset="/img/loading.gif" alt=""></p>
</li>
</ul>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145741.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>但是不能解决指令交错问题</strong></p>
<ul>
<li>写屏障仅仅是保证之后的读能够读到新的结果，但不能保证读跑到它前面去 </li>
<li>而有序性的保证也只是保证了<strong>本线程内</strong>相关代码不被重排序</li>
</ul>
<h3 id="实现原理之Lock前缀"><a href="#实现原理之Lock前缀" class="headerlink" title="实现原理之Lock前缀"></a>实现原理之Lock前缀</h3><p>在X86处理器下通过工具获取JIT编译器生成的汇编指令来查看对volatile进行写操作时</p>
<pre><code class="hljs java">instance = <span class="hljs-keyword">new</span> Singleton();</code></pre>

<p>对应的汇编代码是</p>
<pre><code class="hljs jboss-cli"><span class="hljs-string">...</span> lock addl <span class="hljs-string">...</span></code></pre>

<p>有volatile变量修饰的共享变量进行写操作的时候会多出第二行汇编代码，通过查IA-32架构软件开发者手册可知，<strong>Lock前缀</strong>的指令在多核处理器下会引发了两件事</p>
<ul>
<li>Lock前缀指令会引起处理器<strong>缓存回写到内存</strong><ul>
<li>Lock前缀指令导致在执行指令期间，声言处理器的LOCK#信号。在多处理器环境中，LOCK#信号确保在声言该信号期间，处理器可以独占任何共享内存。但是，在最近的处理器里，LOCK #信号一般不锁总线，而是<strong>锁缓存</strong>，毕竟锁总线开销的比较大。使用缓存一致性机制来确保修改的原子性，此操作被称为“缓存锁定”，<strong>缓存一致性机制会阻止同时修改由两个以上处理器缓存的内存区域数据</strong></li>
</ul>
</li>
<li>一个处理器的缓存回写到内存会<strong>导致其他处理器的缓存无效</strong><ul>
<li>在多核处理器系统中进行操作的时候，IA-32和Intel 64处理器能<strong>嗅探其他处理器访问系统内存和它们的内部缓存</strong>。处理器使用嗅探技术保证它的内部缓存、系统内存和其他处理器的缓存的数据在总线上保持一致</li>
</ul>
</li>
</ul>
<h1 id="五、共享模型之无锁"><a href="#五、共享模型之无锁" class="headerlink" title="五、共享模型之无锁"></a>五、共享模型之无锁</h1><h2 id="1、无锁解决线程安全问题"><a href="#1、无锁解决线程安全问题" class="headerlink" title="1、无锁解决线程安全问题"></a>1、无锁解决线程安全问题</h2><ul>
<li><p>使用<strong>原子整数</strong></p>
<pre><code class="hljs ebnf"><span class="hljs-attribute">AtomicInteger balance</span> = new AtomicInteger();</code></pre>



</li>
</ul>
<pre><code class="hljs java"><span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">Account</span> </span>&#123;
	<span class="hljs-function">Integer <span class="hljs-title">getBalance</span><span class="hljs-params">()</span></span>;

	<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">withdraw</span><span class="hljs-params">(Integer amount)</span></span>;

	<span class="hljs-comment">/**</span>
<span class="hljs-comment">	 * 方法内会启动 1000 个线程，每个线程做 -10 元 的操作     * 如果初始余额为 10000 那么正确的结果应当是 0</span>
<span class="hljs-comment">	 */</span>
	<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">demo</span><span class="hljs-params">(Account account)</span> </span>&#123;
		List&lt;Thread&gt; ts = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();
		<span class="hljs-keyword">long</span> start = System.nanoTime();
		<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">1000</span>; i++) &#123;
			ts.add(<span class="hljs-keyword">new</span> Thread(() -&gt; &#123;
				account.withdraw(<span class="hljs-number">10</span>);
			&#125;));
		&#125;
		ts.forEach(Thread::start);
		ts.forEach(t -&gt; &#123;
			<span class="hljs-keyword">try</span> &#123;
				t.join();
			&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
				e.printStackTrace();
			&#125;
		&#125;);
		<span class="hljs-keyword">long</span> end = System.nanoTime();
		System.out.println(account.getBalance() + <span class="hljs-string">" cost: "</span> + (end - start) / <span class="hljs-number">1000_000</span> + <span class="hljs-string">" ms"</span>);
	&#125;
&#125;

<span class="hljs-comment">//线程不安全的做法</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AccountUnsafe</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Account</span> </span>&#123;
	<span class="hljs-keyword">private</span> Integer balance;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AccountUnsafe</span><span class="hljs-params">(Integer balance)</span> </span>&#123;
		<span class="hljs-keyword">this</span>.balance = balance;
	&#125;


	<span class="hljs-meta">@Override</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> Integer <span class="hljs-title">getBalance</span><span class="hljs-params">()</span> </span>&#123;
		<span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>.balance;
	&#125;

	<span class="hljs-meta">@Override</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">synchronized</span> <span class="hljs-keyword">void</span> <span class="hljs-title">withdraw</span><span class="hljs-params">(Integer amount)</span> </span>&#123;
		balance -= amount;
	&#125;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		Account.demo(<span class="hljs-keyword">new</span> AccountUnsafe(<span class="hljs-number">10000</span>));
		Account.demo(<span class="hljs-keyword">new</span> AccountCas(<span class="hljs-number">10000</span>));
	&#125;
&#125;

<span class="hljs-comment">//线程安全的做法</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AccountCas</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">Account</span> </span>&#123;
	<span class="hljs-comment">//使用原子整数</span>
	<span class="hljs-keyword">private</span> AtomicInteger balance;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">AccountCas</span><span class="hljs-params">(<span class="hljs-keyword">int</span> balance)</span> </span>&#123;
		<span class="hljs-keyword">this</span>.balance = <span class="hljs-keyword">new</span> AtomicInteger(balance);
	&#125;

	<span class="hljs-meta">@Override</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> Integer <span class="hljs-title">getBalance</span><span class="hljs-params">()</span> </span>&#123;
		<span class="hljs-comment">//得到原子整数的值</span>
		<span class="hljs-keyword">return</span> balance.get();
	&#125;

	<span class="hljs-meta">@Override</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">withdraw</span><span class="hljs-params">(Integer amount)</span> </span>&#123;
		<span class="hljs-keyword">while</span>(<span class="hljs-keyword">true</span>) &#123;
			<span class="hljs-comment">//获得修改前的值</span>
			<span class="hljs-keyword">int</span> prev = balance.get();
			<span class="hljs-comment">//获得修改后的值</span>
			<span class="hljs-keyword">int</span> next = prev-amount;
			<span class="hljs-comment">//比较并设值</span>
			<span class="hljs-keyword">if</span>(balance.compareAndSet(prev, next)) &#123;
				<span class="hljs-keyword">break</span>;
			&#125;
		&#125;
	&#125;
&#125;</code></pre>



<h2 id="2、CAS与volatile"><a href="#2、CAS与volatile" class="headerlink" title="2、CAS与volatile"></a>2、CAS与volatile</h2><p>前面看到的 AtomicInteger 的解决方法，内部并没有用锁来保护共享变量的线程安全。那么它是如何实现的呢？</p>
<p>其中的<strong>关键是 compareAndSwap</strong>（比较并设置值），它的<strong>简称就是 CAS</strong> （也有 Compare And Swap 的说法），它必须是<strong>原子操作</strong>。</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145914.png" srcset="/img/loading.gif" alt=""></p>
<h3 id="工作流程"><a href="#工作流程" class="headerlink" title="工作流程"></a><strong>工作流程</strong></h3><ul>
<li>当一个线程要去修改Account对象中的值时，先获取值pre（调用get方法），然后再将其设置为新的值next（调用cas方法）。在调用cas方法时，会将pre与Account中的余额进行比较。<ul>
<li>如果<strong>两者相等</strong>，就说明该值还未被其他线程修改，此时便可以进行修改操作。</li>
<li>如果<strong>两者不相等</strong>，就不设置值，重新获取值pre（调用get方法），然后再将其设置为新的值next（调用cas方法），直到修改成功为止。</li>
</ul>
</li>
</ul>
<p><strong>注意</strong></p>
<ul>
<li><p>其实 CAS 的底层是 <strong>lock cmpxchg</strong> 指令（X86 架构），在单核 CPU 和多核 CPU 下都能够保证【比较-交换】的<strong>原子性</strong>。</p>
</li>
<li><p>在多核状态下，某个核执行到带 lock 的指令时，CPU 会让总线锁住，当这个核把此指令执行完毕，再开启总线。这个过程中不会被线程的调度机制所打断，保证了多个线程对内存操作的准确性，是原子的。</p>
</li>
</ul>
<h3 id="volatile"><a href="#volatile" class="headerlink" title="volatile"></a>volatile</h3><p>获取共享变量时，为了保证该变量的<strong>可见性</strong>，需要使用 <strong>volatile</strong> 修饰。<br>它可以用来修饰成员变量和静态成员变量，他可以避免线程从自己的工作缓存中查找变量的值，必须到<strong>主存中获取</strong> 它的值，线程操作 volatile 变量都是直接操作主存。即一个线程对 volatile 变量的修改，对另一个线程可见。</p>
<p><strong>注意</strong></p>
<pre><code class="hljs cpp"><span class="hljs-keyword">volatile</span> 仅仅保证了共享变量的可见性，让其它线程能够看到新值，但不能解决指令交错问题（不能保证原子性）</code></pre>

<p><strong>CAS 必须借助 volatile</strong> 才能读取到共享变量的新值来实现【比较并交换】的效果 </p>
<h3 id="效率问题"><a href="#效率问题" class="headerlink" title="效率问题"></a>效率问题</h3><p>一般情况下，使用无锁比使用加锁的<strong>效率更高。</strong></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145931.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>原因</strong></p>
<h3 id="CAS特点"><a href="#CAS特点" class="headerlink" title="CAS特点"></a>CAS特点</h3><p>结合 CAS 和 volatile 可以实现<strong>无锁并发</strong>，适用于<strong>线程数少、多核 CPU</strong> 的场景下。</p>
<ul>
<li>CAS 是基于<strong>乐观锁</strong>的思想：乐观的估计，不怕别的线程来修改共享变量，就算改了也没关系，我吃亏点再重试呗。 </li>
<li>synchronized 是基于悲观锁的思想：悲观的估计，得防着其它线程来修改共享变量，我上了锁你们都别想改，我改完了解开锁，你们才有机会。 </li>
<li>CAS 体现的是<strong>无锁并发、无阻塞并发</strong>，请仔细体会这两句话的意思<ul>
<li>因为没有使用 synchronized，所以线程不会陷入阻塞，这是效率提升的因素之一 </li>
<li>但如果竞争激烈，可以想到重试必然频繁发生，反而效率会受影响</li>
</ul>
</li>
</ul>
<h2 id="3、原子整数"><a href="#3、原子整数" class="headerlink" title="3、原子整数"></a>3、原子整数</h2><p>J.U.C 并发包提供了</p>
<ul>
<li>AtomicBoolean</li>
<li>AtomicInteger </li>
<li>AtomicLong</li>
</ul>
<p><strong>以 AtomicInteger 为例</strong></p>
<pre><code class="hljs java"> AtomicInteger i = <span class="hljs-keyword">new</span> AtomicInteger(<span class="hljs-number">0</span>);
 
<span class="hljs-comment">// 获取并自增（i = 0, 结果 i = 1, 返回 0），类似于 i++ System.out.println(i.getAndIncrement());</span>
 
<span class="hljs-comment">// 自增并获取（i = 1, 结果 i = 2, 返回 2），类似于 ++i System.out.println(i.incrementAndGet());</span>
 
<span class="hljs-comment">// 自减并获取（i = 2, 结果 i = 1, 返回 1），类似于 --i System.out.println(i.decrementAndGet());</span>
 
<span class="hljs-comment">// 获取并自减（i = 1, 结果 i = 0, 返回 1），类似于 i--</span>
System.out.println(i.getAndDecrement());
 
<span class="hljs-comment">// 获取并加值（i = 0, 结果 i = 5, 返回 0） </span>
System.out.println(i.getAndAdd(<span class="hljs-number">5</span>));
 
<span class="hljs-comment">// 加值并获取（i = 5, 结果 i = 0, 返回 0） </span>
System.out.println(i.addAndGet(-<span class="hljs-number">5</span>));
 
<span class="hljs-comment">// 获取并更新（i = 0, p 为 i 的当前值, 结果 i = -2, 返回 0） </span>
<span class="hljs-comment">// 其中函数中的操作能保证原子，但函数需要无副作用 </span>
System.out.println(i.getAndUpdate(p -&gt; p - <span class="hljs-number">2</span>));
 
<span class="hljs-comment">// 更新并获取（i = -2, p 为 i 的当前值, 结果 i = 0, 返回 0）</span>
<span class="hljs-comment">// 其中函数中的操作能保证原子，但函数需要无副作用 </span>
System.out.println(i.updateAndGet(p -&gt; p + <span class="hljs-number">2</span>));
 
<span class="hljs-comment">// 获取并计算（i = 0, p 为 i 的当前值, x 为参数1, 结果 i = 10, 返回 0） </span>
<span class="hljs-comment">// 其中函数中的操作能保证原子，但函数需要无副作用 // getAndUpdate 如果在 lambda 中引用了外部的局部变量，要保证该局部变量是 final 的 </span>
<span class="hljs-comment">// getAndAccumulate 可以通过 参数1 来引用外部的局部变量，但因为其不在 lambda 中因此不必是 </span>
<span class="hljs-keyword">final</span> System.out.println(i.getAndAccumulate(<span class="hljs-number">10</span>, (p, x) -&gt; p + x));
 
<span class="hljs-comment">// 计算并获取（i = 10, p 为 i 的当前值, x 为参数1, 结果 i = 0, 返回 0） </span>
<span class="hljs-comment">// 其中函数中的操作能保证原子，但函数需要无副作用</span>
System.out.println(i.accumulateAndGet(-<span class="hljs-number">10</span>, (p, x) -&gt; p + x));</code></pre>



<h2 id="4、原子引用"><a href="#4、原子引用" class="headerlink" title="4、原子引用"></a>4、原子引用</h2><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">interface</span> <span class="hljs-title">DecimalAccount</span> </span>&#123;
	<span class="hljs-function">BigDecimal <span class="hljs-title">getBalance</span><span class="hljs-params">()</span></span>;

	<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">withdraw</span><span class="hljs-params">(BigDecimal amount)</span></span>;

	<span class="hljs-comment">/**</span>
<span class="hljs-comment">	 * 方法内会启动 1000 个线程，每个线程做 -10 元 的操作    </span>
<span class="hljs-comment">     * 如果初始余额为 10000 那么正确的结果应当是 0</span>
<span class="hljs-comment">	 */</span>
	<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">demo</span><span class="hljs-params">(DecimalAccountImpl account)</span> </span>&#123;
		List&lt;Thread&gt; ts = <span class="hljs-keyword">new</span> ArrayList&lt;&gt;();
		<span class="hljs-keyword">long</span> start = System.nanoTime();
		<span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">1000</span>; i++) &#123;
			ts.add(<span class="hljs-keyword">new</span> Thread(() -&gt; &#123;
				account.withdraw(BigDecimal.TEN);
			&#125;));
		&#125;
		ts.forEach(Thread::start);
		ts.forEach(t -&gt; &#123;
			<span class="hljs-keyword">try</span> &#123;
				t.join();
			&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
				e.printStackTrace();
			&#125;
		&#125;);
		<span class="hljs-keyword">long</span> end = System.nanoTime();
		System.out.println(account.getBalance() + <span class="hljs-string">" cost: "</span> + (end - start) / <span class="hljs-number">1000_000</span> + <span class="hljs-string">" ms"</span>);
	&#125;
&#125;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">DecimalAccountImpl</span> <span class="hljs-keyword">implements</span> <span class="hljs-title">DecimalAccount</span> </span>&#123;
	<span class="hljs-comment">//原子引用，泛型类型为小数类型</span>
	AtomicReference&lt;BigDecimal&gt; balance;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">DecimalAccountImpl</span><span class="hljs-params">(BigDecimal balance)</span> </span>&#123;
		<span class="hljs-keyword">this</span>.balance = <span class="hljs-keyword">new</span> AtomicReference&lt;BigDecimal&gt;(balance);
	&#125;

	<span class="hljs-meta">@Override</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> BigDecimal <span class="hljs-title">getBalance</span><span class="hljs-params">()</span> </span>&#123;
		<span class="hljs-keyword">return</span> balance.get();
	&#125;

	<span class="hljs-meta">@Override</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">withdraw</span><span class="hljs-params">(BigDecimal amount)</span> </span>&#123;
		<span class="hljs-keyword">while</span>(<span class="hljs-keyword">true</span>) &#123;
			BigDecimal pre = balance.get();
			BigDecimal next = pre.subtract(amount);
			<span class="hljs-keyword">if</span>(balance.compareAndSet(pre, next)) &#123;
				<span class="hljs-keyword">break</span>;
			&#125;
		&#125;
	&#125;

	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		DecimalAccount.demo(<span class="hljs-keyword">new</span> DecimalAccountImpl(<span class="hljs-keyword">new</span> BigDecimal(<span class="hljs-string">"10000"</span>)));
	&#125;
&#125;</code></pre>



<h2 id="5、ABA问题"><a href="#5、ABA问题" class="headerlink" title="5、ABA问题"></a>5、ABA问题</h2><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo3</span> </span>&#123;
	<span class="hljs-keyword">static</span> AtomicReference&lt;String&gt; str = <span class="hljs-keyword">new</span> AtomicReference&lt;&gt;(<span class="hljs-string">"A"</span>);
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		<span class="hljs-keyword">new</span> Thread(() -&gt; &#123;
			String pre = str.get();
			System.out.println(<span class="hljs-string">"change"</span>);
			<span class="hljs-keyword">try</span> &#123;
				other();
			&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
				e.printStackTrace();
			&#125;
			<span class="hljs-keyword">try</span> &#123;
				Thread.sleep(<span class="hljs-number">1000</span>);
			&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
				e.printStackTrace();
			&#125;
			<span class="hljs-comment">//把str中的A改为C</span>
			System.out.println(<span class="hljs-string">"change A-&gt;C "</span> + str.compareAndSet(pre, <span class="hljs-string">"C"</span>));
		&#125;).start();
	&#125;

	<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">other</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> InterruptedException </span>&#123;
		<span class="hljs-keyword">new</span> Thread(()-&gt; &#123;
			System.out.println(<span class="hljs-string">"change A-&gt;B "</span> + str.compareAndSet(<span class="hljs-string">"A"</span>, <span class="hljs-string">"B"</span>));
		&#125;).start();
		Thread.sleep(<span class="hljs-number">500</span>);
		<span class="hljs-keyword">new</span> Thread(()-&gt; &#123;
			System.out.println(<span class="hljs-string">"change B-&gt;A "</span> + str.compareAndSet(<span class="hljs-string">"B"</span>, <span class="hljs-string">"A"</span>));
		&#125;).start();
	&#125;
&#125;</code></pre>

<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608145952.png" srcset="/img/loading.gif" alt=""></p>
<p>主线程仅能判断出共享变量的值与初值 A <strong>是否相同</strong>，不能感知到这种从 A 改为 B 又 改回 A 的情况，如果主线程希望：<br>只要有其它线程【<strong>动过了</strong>】共享变量，那么自己的 <strong>cas 就算失败</strong>，这时，仅比较值是不够的，需要再加一个<strong>版本号</strong></p>
<h3 id="AtomicStampedReference"><a href="#AtomicStampedReference" class="headerlink" title="AtomicStampedReference"></a><strong>AtomicStampedReference</strong></h3><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo3</span> </span>&#123;
	<span class="hljs-comment">//指定版本号</span>
	<span class="hljs-keyword">static</span> AtomicStampedReference&lt;String&gt; str = <span class="hljs-keyword">new</span> AtomicStampedReference&lt;&gt;(<span class="hljs-string">"A"</span>, <span class="hljs-number">0</span>);
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		<span class="hljs-keyword">new</span> Thread(() -&gt; &#123;
			String pre = str.getReference();
			<span class="hljs-comment">//获得版本号</span>
			<span class="hljs-keyword">int</span> stamp = str.getStamp();
			System.out.println(<span class="hljs-string">"change"</span>);
			<span class="hljs-keyword">try</span> &#123;
				other();
			&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
				e.printStackTrace();
			&#125;
			<span class="hljs-keyword">try</span> &#123;
				Thread.sleep(<span class="hljs-number">1000</span>);
			&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
				e.printStackTrace();
			&#125;
			<span class="hljs-comment">//把str中的A改为C,并比对版本号，如果版本号相同，就执行替换，并让版本号+1</span>
			System.out.println(<span class="hljs-string">"change A-&gt;C stamp "</span> + stamp + str.compareAndSet(pre, <span class="hljs-string">"C"</span>, stamp, stamp+<span class="hljs-number">1</span>));
		&#125;).start();
	&#125;

	<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">other</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> InterruptedException </span>&#123;
		<span class="hljs-keyword">new</span> Thread(()-&gt; &#123;
			<span class="hljs-keyword">int</span> stamp = str.getStamp();
			System.out.println(<span class="hljs-string">"change A-&gt;B stamp "</span> + stamp + str.compareAndSet(<span class="hljs-string">"A"</span>, <span class="hljs-string">"B"</span>, stamp, stamp+<span class="hljs-number">1</span>));
		&#125;).start();
		Thread.sleep(<span class="hljs-number">500</span>);
		<span class="hljs-keyword">new</span> Thread(()-&gt; &#123;
			<span class="hljs-keyword">int</span> stamp = str.getStamp();
			System.out.println(<span class="hljs-string">"change B-&gt;A stamp "</span> + stamp +  str.compareAndSet(<span class="hljs-string">"B"</span>, <span class="hljs-string">"A"</span>, stamp, stamp+<span class="hljs-number">1</span>));
		&#125;).start();
	&#125;
&#125;</code></pre>

<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150003.png" srcset="/img/loading.gif" alt=""></p>
<h3 id="AtomicMarkableReference"><a href="#AtomicMarkableReference" class="headerlink" title="AtomicMarkableReference"></a>AtomicMarkableReference</h3><p>AtomicStampedReference 可以给原子引用加上版本号，追踪原子引用整个的变化过程，如： A -&gt; B -&gt; A -&gt; C ，通过AtomicStampedReference，我们可以知道，引用变量中途被更改了几次。<br>但是有时候，并不关心引用变量更改了几次，只是单纯的关心<strong>是否更改过</strong>，所以就有了 <strong>AtomicMarkableReference</strong></p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo4</span> </span>&#123;
	<span class="hljs-comment">//指定版本号</span>
	<span class="hljs-keyword">static</span> AtomicMarkableReference&lt;String&gt; str = <span class="hljs-keyword">new</span> AtomicMarkableReference&lt;&gt;(<span class="hljs-string">"A"</span>, <span class="hljs-keyword">true</span>);
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
		<span class="hljs-keyword">new</span> Thread(() -&gt; &#123;
			String pre = str.getReference();
			System.out.println(<span class="hljs-string">"change"</span>);
			<span class="hljs-keyword">try</span> &#123;
				other();
			&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
				e.printStackTrace();
			&#125;
			<span class="hljs-keyword">try</span> &#123;
				Thread.sleep(<span class="hljs-number">1000</span>);
			&#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
				e.printStackTrace();
			&#125;
			<span class="hljs-comment">//把str中的A改为C,并比对版本号，如果版本号相同，就执行替换，并让版本号+1</span>
			System.out.println(<span class="hljs-string">"change A-&gt;C mark "</span> +  str.compareAndSet(pre, <span class="hljs-string">"C"</span>, <span class="hljs-keyword">true</span>, <span class="hljs-keyword">false</span>));
		&#125;).start();
	&#125;

	<span class="hljs-function"><span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">other</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> InterruptedException </span>&#123;
		<span class="hljs-keyword">new</span> Thread(() -&gt; &#123;
			System.out.println(<span class="hljs-string">"change A-&gt;A mark "</span> + str.compareAndSet(<span class="hljs-string">"A"</span>, <span class="hljs-string">"A"</span>, <span class="hljs-keyword">true</span>, <span class="hljs-keyword">false</span>));
		&#125;).start();
	&#125;
&#125;</code></pre>



<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150017.png" srcset="/img/loading.gif" alt=""></p>
<h3 id="两者的区别"><a href="#两者的区别" class="headerlink" title="两者的区别"></a>两者的区别</h3><ul>
<li><p><strong>AtomicStampedReference</strong> 需要我们传入<strong>整型变量</strong>作为版本号，来判定是否被更改过</p>
</li>
<li><p><strong>AtomicMarkableReference</strong>需要我们传入<strong>布尔变量</strong>作为标记，来判断是否被更改过</p>
</li>
</ul>
<h2 id="6、原子数组"><a href="#6、原子数组" class="headerlink" title="6、原子数组"></a>6、原子数组</h2><ul>
<li>AtomicIntegerArray</li>
<li>AtomicLongArray </li>
<li>AtomicReferenceArray</li>
</ul>
<h3 id="lamba表达式的使用"><a href="#lamba表达式的使用" class="headerlink" title="lamba表达式的使用"></a>lamba表达式的使用</h3><ul>
<li>提供者 <ul>
<li>无参又返回</li>
<li>()-&gt;返回结果</li>
</ul>
</li>
<li>方法<ul>
<li>有参有返回</li>
<li>(参数一…)-&gt;返回结果</li>
</ul>
</li>
<li>消费者<ul>
<li>有参无返回</li>
<li>(参数一…)-&gt;void</li>
</ul>
</li>
</ul>
<h2 id="7、原子更新器"><a href="#7、原子更新器" class="headerlink" title="7、原子更新器"></a>7、原子更新器</h2><ul>
<li>AtomicReferenceFieldUpdater // 域  字段 </li>
<li>AtomicIntegerFieldUpdater </li>
<li>AtomicLongFieldUpdate</li>
</ul>
<p>原子更新器用于帮助我们改变某个对象中的某个属性</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo1</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      Student student = <span class="hljs-keyword">new</span> Student();
       
      <span class="hljs-comment">// 获得原子更新器</span>
      <span class="hljs-comment">// 泛型</span>
      <span class="hljs-comment">// 参数1 持有属性的类 参数2 被更新的属性的类</span>
      <span class="hljs-comment">// newUpdater中的参数：第三个为属性的名称</span>
      AtomicReferenceFieldUpdater&lt;Student, String&gt; updater = AtomicReferenceFieldUpdater.newUpdater(Student.class, String.class, "name");
       
      <span class="hljs-comment">// 修改</span>
      updater.compareAndSet(student, <span class="hljs-keyword">null</span>, <span class="hljs-string">"Nyima"</span>);
      System.out.println(student);
   &#125;
&#125;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Student</span> </span>&#123;
   <span class="hljs-keyword">volatile</span> String name;

   <span class="hljs-meta">@Override</span>
   <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>&#123;
      <span class="hljs-keyword">return</span> <span class="hljs-string">"Student&#123;"</span> +
            <span class="hljs-string">"name='"</span> + name + <span class="hljs-string">'\''</span> +
            <span class="hljs-string">'&#125;'</span>;
   &#125;
&#125;</code></pre>



<h3 id="原子更新器初始化过程"><a href="#原子更新器初始化过程" class="headerlink" title="原子更新器初始化过程"></a>原子更新器初始化过程</h3><p>从上面的例子可以看出，原子更新器是通过newUpdater来获取实例的。其中传入了三个参数</p>
<ul>
<li>拥有属性的类的Class</li>
<li>属性的Class</li>
<li>属性的名称</li>
</ul>
<p>大概可以猜出来，<strong>初始化过程用到了反射</strong>，让我们看看源码来验证一下这个猜测。</p>
<h4 id="newUpdater方法"><a href="#newUpdater方法" class="headerlink" title="newUpdater方法"></a>newUpdater方法</h4><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> &lt;U,W&gt; <span class="hljs-function">AtomicReferenceFieldUpdater&lt;U,W&gt; <span class="hljs-title">newUpdater</span><span class="hljs-params">(Class&lt;U&gt; tclass,</span></span>
<span class="hljs-function"><span class="hljs-params">                                                                Class&lt;W&gt; vclass,</span></span>
<span class="hljs-function"><span class="hljs-params">                                                                String fieldName)</span> </span>&#123;
    <span class="hljs-comment">// 返回了一个AtomicReferenceFieldUpdaterImpl实例</span>
    <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> AtomicReferenceFieldUpdaterImpl&lt;U,W&gt;
        (tclass, vclass, fieldName, Reflection.getCallerClass());
&#125;</code></pre>

<p>从newUpdater方法还并不能看出来具体的初始化过程</p>
<h4 id="内部实现类"><a href="#内部实现类" class="headerlink" title="内部实现类"></a>内部实现类</h4><p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201020145006.png" srcset="/img/loading.gif" alt=""></p>
<p>AtomicReferenceFieldUpdater为抽象类，该类<strong>内部有一个自己的实现类AtomicReferenceFieldUpdaterImpl</strong></p>
<pre><code class="hljs java"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AtomicReferenceFieldUpdaterImpl</span>&lt;<span class="hljs-title">T</span>,<span class="hljs-title">V</span>&gt;</span>
<span class="hljs-class">        <span class="hljs-keyword">extends</span> <span class="hljs-title">AtomicReferenceFieldUpdater</span>&lt;<span class="hljs-title">T</span>,<span class="hljs-title">V</span>&gt;</span></code></pre>

<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201020145119.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>构造方法</strong></p>
<pre><code class="hljs java">AtomicReferenceFieldUpdaterImpl(<span class="hljs-keyword">final</span> Class&lt;T&gt; tclass,
                                <span class="hljs-keyword">final</span> Class&lt;V&gt; vclass,
                                <span class="hljs-keyword">final</span> String fieldName,
                                <span class="hljs-keyword">final</span> Class&lt;?&gt; caller) &#123;
    <span class="hljs-comment">// 用于保存要被修改的属性</span>
    <span class="hljs-keyword">final</span> Field field;
    
    <span class="hljs-comment">// 属性的Class</span>
    <span class="hljs-keyword">final</span> Class&lt;?&gt; fieldClass;
    
    <span class="hljs-comment">// field的修饰符</span>
    <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> modifiers;
    <span class="hljs-keyword">try</span> &#123;
        <span class="hljs-comment">// 反射获得属性</span>
        field = AccessController.doPrivileged(
            <span class="hljs-keyword">new</span> PrivilegedExceptionAction&lt;Field&gt;() &#123;
                <span class="hljs-function"><span class="hljs-keyword">public</span> Field <span class="hljs-title">run</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> NoSuchFieldException </span>&#123;
                    <span class="hljs-comment">// tclass为传入的属性的Class，可以通过它来获得属性</span>
                    <span class="hljs-keyword">return</span> tclass.getDeclaredField(fieldName);
                &#125;
            &#125;);
        
        <span class="hljs-comment">// 获得属性的修饰符，主要用于判断</span>
        <span class="hljs-comment">// 1、vclass 与 属性确切的类型是否匹配</span>
        <span class="hljs-comment">// 2、是否为引用类型</span>
        <span class="hljs-comment">// 3、被修改的属性是否加了volatile关键字</span>
        modifiers = field.getModifiers();
        sun.reflect.misc.ReflectUtil.ensureMemberAccess(
            caller, tclass, <span class="hljs-keyword">null</span>, modifiers);
        ClassLoader cl = tclass.getClassLoader();
        ClassLoader ccl = caller.getClassLoader();
        <span class="hljs-keyword">if</span> ((ccl != <span class="hljs-keyword">null</span>) &amp;&amp; (ccl != cl) &amp;&amp;
            ((cl == <span class="hljs-keyword">null</span>) || !isAncestor(cl, ccl))) &#123;
            sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass);
        &#125;
        
        <span class="hljs-comment">// 获得属性类的Class</span>
        fieldClass = field.getType();
    &#125; <span class="hljs-keyword">catch</span> (PrivilegedActionException pae) &#123;
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(pae.getException());
    &#125; <span class="hljs-keyword">catch</span> (Exception ex) &#123;
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> RuntimeException(ex);
    &#125;

    <span class="hljs-keyword">if</span> (vclass != fieldClass)
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> ClassCastException();
    <span class="hljs-keyword">if</span> (vclass.isPrimitive())
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Must be reference type"</span>);

    <span class="hljs-keyword">if</span> (!Modifier.isVolatile(modifiers))
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalArgumentException(<span class="hljs-string">"Must be volatile type"</span>);

    <span class="hljs-comment">// Access to protected field members is restricted to receivers only</span>
    <span class="hljs-comment">// of the accessing class, or one of its subclasses, and the</span>
    <span class="hljs-comment">// accessing class must in turn be a subclass (or package sibling)</span>
    <span class="hljs-comment">// of the protected member's defining class.</span>
    <span class="hljs-comment">// If the updater refers to a protected field of a declaring class</span>
    <span class="hljs-comment">// outside the current package, the receiver argument will be</span>
    <span class="hljs-comment">// narrowed to the type of the accessing class.</span>
 	<span class="hljs-comment">// 对类中的属性进行初始化</span>
    <span class="hljs-keyword">this</span>.cclass = (Modifier.isProtected(modifiers) &amp;&amp;
                   tclass.isAssignableFrom(caller) &amp;&amp;
                   !isSamePackage(tclass, caller))
                  ? caller : tclass;
    <span class="hljs-keyword">this</span>.tclass = tclass;
    <span class="hljs-keyword">this</span>.vclass = vclass;
    <span class="hljs-comment">// 获得偏移量</span>
    <span class="hljs-keyword">this</span>.offset = U.objectFieldOffset(field);
&#125;</code></pre>

<p><strong>可以看出，原子引用更新器确实使用了反射</strong></p>
<h2 id="8、LongAdder原理"><a href="#8、LongAdder原理" class="headerlink" title="8、LongAdder原理"></a>8、LongAdder原理</h2><h3 id="原理之伪共享"><a href="#原理之伪共享" class="headerlink" title="原理之伪共享"></a>原理之伪共享</h3><p>​    <img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150037.png" srcset="/img/loading.gif" alt=""></p>
<p>缓存行伪共享得从缓存说起<br>缓存与内存的速度比较</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150051.png" srcset="/img/loading.gif" alt=""></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150102.png" srcset="/img/loading.gif" alt=""></p>
<p>因为 CPU 与 内存的速度差异很大，需要靠预读数据至<strong>缓存</strong>来提升效率。<br>而缓存以<strong>缓存行</strong>为单位，每个缓存行对应着一块内存，一般是 <strong>64 byte</strong>（8 个 long）<br>缓存的加入会造成数据副本的产生，即同一份数据会缓存在不同核心的缓存行中<br>CPU 要保证数据的<strong>一致性</strong>，如果某个 CPU 核心<strong>更改</strong>了数据，其它 CPU 核心对应的整个缓存行必须<strong>失效</strong></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150111.png" srcset="/img/loading.gif" alt=""></p>
<p>因为 Cell 是数组形式，在内存中是连续存储的，一个 Cell 为 24 字节（16 字节的对象头和 8 字节的 value），因 此缓存行可以存下 2 个的 Cell 对象。这样问题来了：</p>
<ul>
<li>Core-0 要修改 Cell[0]</li>
<li>Core-1 要修改 Cell[1] </li>
</ul>
<p>无论谁修改成功，都会导致对方 Core 的缓存行失效，</p>
<p>比如 Core-0 中 Cell[0]=6000, Cell[1]=8000 要累加 Cell[0]=6001, Cell[1]=8000 ，这时会让 Core-1 的缓存行失效</p>
<p>@sun.misc.Contended 用来解决这个问题，它的原理是在使用此注解的对象或字段的<strong>前后各增加 128 字节大小的 padding</strong>（空白），从而让 CPU 将对象预读至缓存时<strong>占用不同的缓存行</strong>，这样，不会造成对方缓存行的失效</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150119.png" srcset="/img/loading.gif" alt=""></p>
<p><strong>累加主要调用以下方法</strong></p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">add</span><span class="hljs-params">(<span class="hljs-keyword">long</span> x)</span> </span>&#123;
       Cell[] as; <span class="hljs-keyword">long</span> b, v; <span class="hljs-keyword">int</span> m; Cell a;
       <span class="hljs-keyword">if</span> ((as = cells) != <span class="hljs-keyword">null</span> || !casBase(b = base, b + x)) &#123;
           <span class="hljs-keyword">boolean</span> uncontended = <span class="hljs-keyword">true</span>;
           <span class="hljs-keyword">if</span> (as == <span class="hljs-keyword">null</span> || (m = as.length - <span class="hljs-number">1</span>) &lt; <span class="hljs-number">0</span> ||
               (a = as[getProbe() &amp; m]) == <span class="hljs-keyword">null</span> ||
               !(uncontended = a.cas(v = a.value, v + x)))
               longAccumulate(x, <span class="hljs-keyword">null</span>, uncontended);
       &#125;
   &#125;</code></pre>

<p><strong>累加流程图</strong></p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20200608150129.png" srcset="/img/loading.gif" alt=""></p>
<h2 id="9、Unsafe"><a href="#9、Unsafe" class="headerlink" title="9、Unsafe"></a>9、Unsafe</h2><p>Unsafe 对象提供了非常底层的，操作内存、线程的方法，Unsafe 对象不能直接调用，只能通过<strong>反射</strong>获得</p>
<pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">GetUnsafe</span> </span>&#123;
	<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> <span class="hljs-keyword">throws</span> NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchFieldException </span>&#123;
		<span class="hljs-comment">// 通过反射获得Unsafe对象</span>
		Class unsafeClass = Unsafe<span class="hljs-class">.<span class="hljs-keyword">class</span></span>;
		<span class="hljs-comment">// 获得构造函数，Unsafe的构造函数为私有的</span>
		Constructor constructor = unsafeClass.getDeclaredConstructor();
		<span class="hljs-comment">// 设置为允许访问私有内容</span>
		constructor.setAccessible(<span class="hljs-keyword">true</span>);
		<span class="hljs-comment">// 创建Unsafe对象</span>
		Unsafe unsafe = (Unsafe) constructor.newInstance();
		
		<span class="hljs-comment">// 创建Person对象</span>
		Person person = <span class="hljs-keyword">new</span> Person();
		<span class="hljs-comment">// 获得其属性 name 的偏移量</span>
		Field field = Person.class.getDeclaredField("name");
		<span class="hljs-keyword">long</span> offset = unsafe.objectFieldOffset(field);

		<span class="hljs-comment">// 通过unsafe的CAS操作改变值</span>
		unsafe.compareAndSwapObject(person, offset, <span class="hljs-keyword">null</span>, <span class="hljs-string">"Nyima"</span>);
		System.out.println(person);
	&#125;
&#125;

<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Person</span> </span>&#123;
    <span class="hljs-comment">// 配合CAS操作，必须用volatile修饰</span>
 	<span class="hljs-keyword">volatile</span> String name;


	<span class="hljs-meta">@Override</span>
	<span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">toString</span><span class="hljs-params">()</span> </span>&#123;
		<span class="hljs-keyword">return</span> <span class="hljs-string">"Person&#123;"</span> +
				<span class="hljs-string">"name='"</span> + name + <span class="hljs-string">'\''</span> +
				<span class="hljs-string">'&#125;'</span>;
	&#125;
&#125;</code></pre>







<h1 id="六、共享模型之不可变"><a href="#六、共享模型之不可变" class="headerlink" title="六、共享模型之不可变"></a>六、共享模型之不可变</h1><h3 id="1、不可变"><a href="#1、不可变" class="headerlink" title="1、不可变"></a>1、不可变</h3><p>如果一个对象在<strong>不能够修</strong>改其内部状态（属性），那么它就是线程安全的，因为不存在并发修改。</p>
<h3 id="2、不可变设计"><a href="#2、不可变设计" class="headerlink" title="2、不可变设计"></a>2、不可变设计</h3><h4 id="String类中不可变的体现"><a href="#String类中不可变的体现" class="headerlink" title="String类中不可变的体现"></a>String类中不可变的体现</h4><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">String</span></span>
<span class="hljs-class">    <span class="hljs-keyword">implements</span> <span class="hljs-title">java</span>.<span class="hljs-title">io</span>.<span class="hljs-title">Serializable</span>, <span class="hljs-title">Comparable</span>&lt;<span class="hljs-title">String</span>&gt;, <span class="hljs-title">CharSequence</span> </span>&#123;
    <span class="hljs-comment">/** The value is used for character storage. */</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">char</span> value[];

    <span class="hljs-comment">/** Cache the hash code for the string */</span>
    <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> hash; <span class="hljs-comment">// Default to 0</span>
    
   <span class="hljs-comment">//....</span>
  &#125;
&#125;</code></pre>



<p><strong>ﬁnal 的使用 **<br>发现该类、类中所有属性都是 **ﬁnal</strong> 的</p>
<ul>
<li>属性用 ﬁnal 修饰保证了该属性是只读的，不能修改 </li>
<li>类用 ﬁnal 修饰保证了该类中的方法不能被覆盖，<strong>防止子类无意间破坏不可变性</strong></li>
</ul>
<p>*<em>保护性拷贝 *</em></p>
<p>但有同学会说，使用字符串时，也有一些跟修改相关的方法啊，比如 substring 等，那么下面就看一看这些方法是 如何实现的，就以 substring 为例</p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">substring</span><span class="hljs-params">(<span class="hljs-keyword">int</span> beginIndex)</span> </span>&#123;
        <span class="hljs-keyword">if</span> (beginIndex &lt; <span class="hljs-number">0</span>) &#123;
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> StringIndexOutOfBoundsException(beginIndex);
        &#125;
        <span class="hljs-keyword">int</span> subLen = value.length - beginIndex;
        <span class="hljs-keyword">if</span> (subLen &lt; <span class="hljs-number">0</span>) &#123;
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> StringIndexOutOfBoundsException(subLen);
        &#125;
    	<span class="hljs-comment">//返回的是一个新的对象</span>
        <span class="hljs-keyword">return</span> (beginIndex == <span class="hljs-number">0</span>) ? <span class="hljs-keyword">this</span> : <span class="hljs-keyword">new</span> String(value, beginIndex, subLen);
    &#125;</code></pre>

<p>发现其内部是调用 String 的构造方法<strong>创建了一个新字符串</strong></p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">String</span><span class="hljs-params">(<span class="hljs-keyword">char</span> value[], <span class="hljs-keyword">int</span> offset, <span class="hljs-keyword">int</span> count)</span> </span>&#123;
        <span class="hljs-keyword">if</span> (offset &lt; <span class="hljs-number">0</span>) &#123;
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> StringIndexOutOfBoundsException(offset);
        &#125;
        <span class="hljs-keyword">if</span> (count &lt;= <span class="hljs-number">0</span>) &#123;
            <span class="hljs-keyword">if</span> (count &lt; <span class="hljs-number">0</span>) &#123;
                <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> StringIndexOutOfBoundsException(count);
            &#125;
            <span class="hljs-keyword">if</span> (offset &lt;= value.length) &#123;
                <span class="hljs-keyword">this</span>.value = <span class="hljs-string">""</span>.value;
                <span class="hljs-keyword">return</span>;
            &#125;
        &#125;
        <span class="hljs-comment">// Note: offset or count might be near -1&gt;&gt;&gt;1.</span>
        <span class="hljs-keyword">if</span> (offset &gt; value.length - count) &#123;
            <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> StringIndexOutOfBoundsException(offset + count);
        &#125;
        <span class="hljs-keyword">this</span>.value = Arrays.copyOfRange(value, offset, offset+count);
    &#125;</code></pre>

<p>构造新字符串对象时，会生成新的 char[] value，对内容进行复制 。这种通过创建副本对象来避免共享的手段称之为【<strong>保护性拷贝</strong>（defensive copy）】</p>
<h1 id="七、线程池"><a href="#七、线程池" class="headerlink" title="七、线程池"></a>七、线程池</h1><h2 id="1、自定义线程池"><a href="#1、自定义线程池" class="headerlink" title="1、自定义线程池"></a>1、自定义线程池</h2><h3 id="图解"><a href="#图解" class="headerlink" title="图解"></a>图解</h3><p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201021154837.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li>阻塞队列中维护了由主线程（或者其他线程）所产生的的任务</li>
<li>主线程类似于<strong>生产者</strong>，产生任务并放入阻塞队列中</li>
<li>线程池类似于<strong>消费者</strong>，得到阻塞队列中已有的任务并执行</li>
</ul>
<h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo3</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      ThreadPool threadPool = <span class="hljs-keyword">new</span> ThreadPool(<span class="hljs-number">2</span>,  TimeUnit.SECONDS, <span class="hljs-number">1</span>, <span class="hljs-number">4</span>);
      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10</span>; i++) &#123;
         threadPool.execute(()-&gt;&#123;
            <span class="hljs-keyword">try</span> &#123;
               TimeUnit.SECONDS.sleep(<span class="hljs-number">10000</span>);
            &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
               e.printStackTrace();
            &#125;
            System.out.println(<span class="hljs-string">"任务正在执行!"</span>);
         &#125;);
      &#125;
   &#125;
&#125;


<span class="hljs-comment">/**</span>
<span class="hljs-comment"> * 自定义线程池</span>
<span class="hljs-comment"> */</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">ThreadPool</span> </span>&#123;
   <span class="hljs-comment">/**</span>
<span class="hljs-comment">    * 自定义阻塞队列</span>
<span class="hljs-comment">    */</span>
   <span class="hljs-keyword">private</span> BlockingQueue&lt;Runnable&gt; blockingQueue;

   <span class="hljs-comment">/**</span>
<span class="hljs-comment">    * 核心线程数</span>
<span class="hljs-comment">    */</span>
   <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> coreSize;

   <span class="hljs-keyword">private</span> HashSet&lt;Worker&gt; workers = <span class="hljs-keyword">new</span> HashSet&lt;&gt;();

   <span class="hljs-comment">/**</span>
<span class="hljs-comment">    * 用于指定线程最大存活时间</span>
<span class="hljs-comment">    */</span>
   <span class="hljs-keyword">private</span> TimeUnit timeUnit;
   <span class="hljs-keyword">private</span> <span class="hljs-keyword">long</span> timeout;

   <span class="hljs-comment">/**</span>
<span class="hljs-comment">    * 工作线程类</span>
<span class="hljs-comment">    * 内部封装了Thread类，并且添加了一些属性</span>
<span class="hljs-comment">    */</span>
   <span class="hljs-keyword">private</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Worker</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Thread</span> </span>&#123;
      Runnable task;

      <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Worker</span><span class="hljs-params">(Runnable task)</span> </span>&#123;
         System.out.println(<span class="hljs-string">"初始化任务"</span>);
         <span class="hljs-keyword">this</span>.task = task;
      &#125;

      <span class="hljs-meta">@Override</span>
      <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>&#123;
         <span class="hljs-comment">// 如果有任务就执行</span>
         <span class="hljs-comment">// 如果阻塞队列中有任务，就继续执行</span>
         <span class="hljs-keyword">while</span> (task != <span class="hljs-keyword">null</span> || (task = blockingQueue.take()) != <span class="hljs-keyword">null</span>) &#123;
            <span class="hljs-keyword">try</span> &#123;
               System.out.println(<span class="hljs-string">"执行任务"</span>);
               task.run();
            &#125; <span class="hljs-keyword">catch</span> (Exception e) &#123;
               e.printStackTrace();
            &#125; <span class="hljs-keyword">finally</span> &#123;
               <span class="hljs-comment">// 任务执行完毕，设为空</span>
               System.out.println(<span class="hljs-string">"任务执行完毕"</span>);
               task = <span class="hljs-keyword">null</span>;
            &#125;
         &#125;
         <span class="hljs-comment">// 移除任务</span>
         <span class="hljs-keyword">synchronized</span> (workers) &#123;
            System.out.println(<span class="hljs-string">"移除任务"</span>);
            workers.remove(<span class="hljs-keyword">this</span>);
         &#125;
      &#125;
   &#125;

   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ThreadPool</span><span class="hljs-params">(<span class="hljs-keyword">int</span> coreSize, TimeUnit timeUnit, <span class="hljs-keyword">long</span> timeout, <span class="hljs-keyword">int</span> capacity)</span> </span>&#123;
      <span class="hljs-keyword">this</span>.coreSize = coreSize;
      <span class="hljs-keyword">this</span>.timeUnit = timeUnit;
      blockingQueue = <span class="hljs-keyword">new</span> BlockingQueue&lt;&gt;(capacity);
      <span class="hljs-keyword">this</span>.timeout = timeout;
   &#125;

   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">execute</span><span class="hljs-params">(Runnable task)</span> </span>&#123;
      <span class="hljs-keyword">synchronized</span> (workers) &#123;
         <span class="hljs-comment">// 创建任务</span>
         <span class="hljs-comment">// 池中还有空余线程时，可以运行任务</span>
         <span class="hljs-comment">// 否则阻塞</span>
         <span class="hljs-keyword">if</span> (workers.size() &lt; coreSize) &#123;
            Worker worker = <span class="hljs-keyword">new</span> Worker(task);
            workers.add(worker);
            worker.start();
         &#125; <span class="hljs-keyword">else</span> &#123;
            System.out.println(<span class="hljs-string">"线程池中线程已用完，请稍等"</span>);
            blockingQueue.put(task);
         &#125;
      &#125;
   &#125;
&#125;

<span class="hljs-comment">/**</span>
<span class="hljs-comment"> * 阻塞队列</span>
<span class="hljs-comment"> * 用于存放主线程或其他线程产生的任务</span>
<span class="hljs-comment"> */</span>
<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">BlockingQueue</span>&lt;<span class="hljs-title">T</span>&gt; </span>&#123;
   <span class="hljs-comment">/**</span>
<span class="hljs-comment">    * 阻塞队列</span>
<span class="hljs-comment">    */</span>
   <span class="hljs-keyword">private</span>  Deque&lt;T&gt; blockingQueue;

   <span class="hljs-comment">/**</span>
<span class="hljs-comment">    * 阻塞队列容量</span>
<span class="hljs-comment">    */</span>
   <span class="hljs-keyword">private</span> <span class="hljs-keyword">int</span> capacity;

   <span class="hljs-comment">/**</span>
<span class="hljs-comment">    * 锁</span>
<span class="hljs-comment">    */</span>
   <span class="hljs-keyword">private</span> ReentrantLock lock;

   <span class="hljs-comment">/**</span>
<span class="hljs-comment">    * 条件队列</span>
<span class="hljs-comment">    */</span>
   <span class="hljs-keyword">private</span> Condition fullQueue;
   <span class="hljs-keyword">private</span> Condition emptyQueue;


   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">BlockingQueue</span><span class="hljs-params">(<span class="hljs-keyword">int</span> capacity)</span> </span>&#123;
      blockingQueue = <span class="hljs-keyword">new</span> ArrayDeque&lt;&gt;(capacity);
      lock = <span class="hljs-keyword">new</span> ReentrantLock();
      fullQueue = lock.newCondition();
      emptyQueue = lock.newCondition();
      <span class="hljs-keyword">this</span>.capacity = capacity;
   &#125;

   <span class="hljs-comment">/**</span>
<span class="hljs-comment">    * 获取任务的方法</span>
<span class="hljs-comment">    */</span>
   <span class="hljs-function"><span class="hljs-keyword">public</span> T <span class="hljs-title">take</span><span class="hljs-params">()</span> </span>&#123;
      <span class="hljs-comment">// 加锁</span>
      lock.lock();
      <span class="hljs-keyword">try</span> &#123;
         <span class="hljs-comment">// 如果阻塞队列为空（没有任务），就一直等待</span>
         <span class="hljs-keyword">while</span> (blockingQueue.isEmpty()) &#123;
            <span class="hljs-keyword">try</span> &#123;
               emptyQueue.await();
            &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
               e.printStackTrace();
            &#125;
         &#125;
         <span class="hljs-comment">// 获取任务并唤醒生产者线程</span>
         T task = blockingQueue.removeFirst();
         fullQueue.signalAll();
         <span class="hljs-keyword">return</span> task;
      &#125; <span class="hljs-keyword">finally</span> &#123;
         lock.unlock();
      &#125;
   &#125;

   <span class="hljs-function"><span class="hljs-keyword">public</span> T <span class="hljs-title">takeNanos</span><span class="hljs-params">(<span class="hljs-keyword">long</span> timeout, TimeUnit unit)</span> </span>&#123;
      <span class="hljs-comment">// 转换等待时间</span>
      lock.lock();
      <span class="hljs-keyword">try</span> &#123;
         <span class="hljs-keyword">long</span> nanos = unit.toNanos(timeout);
         <span class="hljs-keyword">while</span> (blockingQueue.isEmpty()) &#123;
            <span class="hljs-keyword">try</span> &#123;
               <span class="hljs-comment">// awaitNanos会返回剩下的等待时间</span>
               nanos = emptyQueue.awaitNanos(nanos);
               <span class="hljs-keyword">if</span> (nanos &lt; <span class="hljs-number">0</span>) &#123;
                  <span class="hljs-keyword">return</span> <span class="hljs-keyword">null</span>;
               &#125;
            &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
               e.printStackTrace();
            &#125;
         &#125;
         T task = blockingQueue.removeFirst();
         fullQueue.signalAll();
         <span class="hljs-keyword">return</span> task;
      &#125; <span class="hljs-keyword">finally</span> &#123;
         lock.unlock();
      &#125;
   &#125;

   <span class="hljs-comment">/**</span>
<span class="hljs-comment">    * 放入任务的方法</span>
<span class="hljs-comment">    * <span class="hljs-doctag">@param</span> task 放入阻塞队列的任务</span>
<span class="hljs-comment">    */</span>
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">put</span><span class="hljs-params">(T task)</span> </span>&#123;
      lock.lock();
      <span class="hljs-keyword">try</span> &#123;
         <span class="hljs-keyword">while</span> (blockingQueue.size() == capacity) &#123;
            <span class="hljs-keyword">try</span> &#123;
               System.out.println(<span class="hljs-string">"阻塞队列已满"</span>);
               fullQueue.await();
            &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
               e.printStackTrace();
            &#125;
         &#125;
         blockingQueue.add(task);
         <span class="hljs-comment">// 唤醒等待的消费者</span>
         emptyQueue.signalAll();
      &#125; <span class="hljs-keyword">finally</span> &#123;
         lock.unlock();
      &#125;
   &#125;

   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">int</span> <span class="hljs-title">getSize</span><span class="hljs-params">()</span> </span>&#123;
      lock.lock();
      <span class="hljs-keyword">try</span> &#123;
         <span class="hljs-keyword">return</span> blockingQueue.size();
      &#125; <span class="hljs-keyword">finally</span> &#123;
         lock.unlock();
      &#125;
   &#125;
&#125;</code></pre>

<p>实现了一个简单的线程池</p>
<ul>
<li>阻塞队列BlockingQueue用于暂存来不及被线程执行的任务<ul>
<li>也可以说是平衡生产者和消费者执行速度上的差异</li>
<li>里面的获取任务和放入任务用到了<strong>生产者消费者模式</strong></li>
</ul>
</li>
<li>线程池中对线程Thread进行了再次的封装，封装为了Worker<ul>
<li>在调用任务的run方法时，线程会去执行该任务，执行完毕后还会<strong>到阻塞队列中获取新任务来执行</strong></li>
</ul>
</li>
<li>线程池中执行任务的主要方法为execute方法<ul>
<li>执行时要判断正在执行的线程数是否大于了线程池容量</li>
</ul>
</li>
</ul>
<h2 id="2、ThreadPoolExecutor"><a href="#2、ThreadPoolExecutor" class="headerlink" title="2、ThreadPoolExecutor"></a>2、ThreadPoolExecutor</h2><h3 id="继承关系"><a href="#继承关系" class="headerlink" title="继承关系"></a>继承关系</h3><p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201022212832.png" srcset="/img/loading.gif" alt=""></p>
<h3 id="线程池状态"><a href="#线程池状态" class="headerlink" title="线程池状态"></a>线程池状态</h3><pre><code class="hljs java"><span class="hljs-comment">// 线程池状态</span>
<span class="hljs-comment">// runState is stored in the high-order bits</span>
<span class="hljs-comment">// RUNNING 高3位为111</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> RUNNING    = -<span class="hljs-number">1</span> &lt;&lt; COUNT_BITS;

<span class="hljs-comment">// SHUTDOWN 高3位为000</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> SHUTDOWN   =  <span class="hljs-number">0</span> &lt;&lt; COUNT_BITS;

<span class="hljs-comment">// 高3位 001</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> STOP       =  <span class="hljs-number">1</span> &lt;&lt; COUNT_BITS;

<span class="hljs-comment">// 高3位 010</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> TIDYING    =  <span class="hljs-number">2</span> &lt;&lt; COUNT_BITS;

<span class="hljs-comment">// 高3位 011</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> TERMINATED =  <span class="hljs-number">3</span> &lt;&lt; COUNT_BITS;</code></pre>

<table>
<thead>
<tr>
<th>状态名称</th>
<th>高3位的值</th>
<th>描述</th>
</tr>
</thead>
<tbody><tr>
<td>RUNNING</td>
<td>111</td>
<td>接收新任务，同时处理任务队列中的任务</td>
</tr>
<tr>
<td>SHUTDOWN</td>
<td>000</td>
<td>不接受新任务，但是处理任务队列中的任务</td>
</tr>
<tr>
<td>STOP</td>
<td>001</td>
<td>中断正在执行的任务，同时抛弃阻塞队列中的任务</td>
</tr>
<tr>
<td>TIDYING</td>
<td>010</td>
<td>任务执行完毕，活动线程为0时，即将进入终结阶段</td>
</tr>
<tr>
<td>TERMINATED</td>
<td>011</td>
<td>终结状态</td>
</tr>
</tbody></table>
<p>线程池状态和线程池中线程的数量<strong>由一个原子整型ctl来共同表示</strong></p>
<ul>
<li>使用一个数来表示两个值的主要原因是：<strong>可以通过一次CAS同时更改两个属性的值</strong></li>
</ul>
<pre><code class="hljs java"><span class="hljs-comment">// 原子整数，前3位保存了线程池的状态，剩余位保存的是线程数量</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> AtomicInteger ctl = <span class="hljs-keyword">new</span> AtomicInteger(ctlOf(RUNNING, <span class="hljs-number">0</span>));

<span class="hljs-comment">// 并不是所有平台的int都是32位。</span>
<span class="hljs-comment">// 去掉前三位保存线程状态的位数，剩下的用于保存线程数量</span>
<span class="hljs-comment">// 高3位为0，剩余位数全为1</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> COUNT_BITS = Integer.SIZE - <span class="hljs-number">3</span>;

<span class="hljs-comment">// 2^COUNT_BITS次方，表示可以保存的最大线程数</span>
<span class="hljs-comment">// CAPACITY 的高3位为 0</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">final</span> <span class="hljs-keyword">int</span> CAPACITY   = (<span class="hljs-number">1</span> &lt;&lt; COUNT_BITS) - <span class="hljs-number">1</span>;</code></pre>

<p>获取线程池状态、线程数量以及合并两个值的操作</p>
<pre><code class="hljs java"><span class="hljs-comment">// Packing and unpacking ctl</span>
<span class="hljs-comment">// 获取运行状态</span>
<span class="hljs-comment">// 该操作会让除高3位以外的数全部变为0</span>
<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">runStateOf</span><span class="hljs-params">(<span class="hljs-keyword">int</span> c)</span>     </span>&#123; <span class="hljs-keyword">return</span> c &amp; ~CAPACITY; &#125;

<span class="hljs-comment">// 获取运行线程数</span>
<span class="hljs-comment">// 该操作会让高3位为0</span>
<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">workerCountOf</span><span class="hljs-params">(<span class="hljs-keyword">int</span> c)</span>  </span>&#123; <span class="hljs-keyword">return</span> c &amp; CAPACITY; &#125;

<span class="hljs-comment">// 计算ctl新值</span>
<span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> <span class="hljs-title">ctlOf</span><span class="hljs-params">(<span class="hljs-keyword">int</span> rs, <span class="hljs-keyword">int</span> wc)</span> </span>&#123; <span class="hljs-keyword">return</span> rs | wc; &#125;</code></pre>



<h3 id="线程池属性"><a href="#线程池属性" class="headerlink" title="线程池属性"></a>线程池属性</h3><pre><code class="hljs java"><span class="hljs-comment">// 工作线程，内部封装了Thread</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Worker</span></span>
<span class="hljs-class">        <span class="hljs-keyword">extends</span> <span class="hljs-title">AbstractQueuedSynchronizer</span></span>
<span class="hljs-class">        <span class="hljs-keyword">implements</span> <span class="hljs-title">Runnable</span> </span>&#123;
    ...
&#125;

<span class="hljs-comment">// 阻塞队列，用于存放来不及被核心线程执行的任务</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> BlockingQueue&lt;Runnable&gt; workQueue;

<span class="hljs-comment">// 锁</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> ReentrantLock mainLock = <span class="hljs-keyword">new</span> ReentrantLock();

<span class="hljs-comment">//  用于存放核心线程的容器，只有当持有锁时才能够获取其中的元素（核心线程）</span>
<span class="hljs-keyword">private</span> <span class="hljs-keyword">final</span> HashSet&lt;Worker&gt; workers = <span class="hljs-keyword">new</span> HashSet&lt;Worker&gt;();</code></pre>



<h3 id="构造方法极其参数"><a href="#构造方法极其参数" class="headerlink" title="构造方法极其参数"></a>构造方法极其参数</h3><p><strong>ThreadPoolExecutor最全面的构造方法</strong></p>
<p>也是构造自定义线程池的方法</p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">ThreadPoolExecutor</span><span class="hljs-params">(<span class="hljs-keyword">int</span> corePoolSize,</span></span>
<span class="hljs-function"><span class="hljs-params">                          <span class="hljs-keyword">int</span> maximumPoolSize,</span></span>
<span class="hljs-function"><span class="hljs-params">                          <span class="hljs-keyword">long</span> keepAliveTime,</span></span>
<span class="hljs-function"><span class="hljs-params">                          TimeUnit unit,</span></span>
<span class="hljs-function"><span class="hljs-params">                          BlockingQueue&lt;Runnable&gt; workQueue,</span></span>
<span class="hljs-function"><span class="hljs-params">                          ThreadFactory threadFactory,</span></span>
<span class="hljs-function"><span class="hljs-params">                          RejectedExecutionHandler handler)</span></span></code></pre>

<h4 id="参数解释"><a href="#参数解释" class="headerlink" title="参数解释"></a><strong>参数解释</strong></h4><ul>
<li>corePoolSize：核心线程数</li>
<li>maximumPoolSize：最大线程数<ul>
<li>maximumPoolSize - corePoolSize = 救急线程数</li>
</ul>
</li>
<li>keepAliveTime：救急线程空闲时的最大生存时间</li>
<li>unit：时间单位</li>
<li>workQueue：阻塞队列（存放任务）<ul>
<li>有界阻塞队列 ArrayBlockingQueue</li>
<li>无界阻塞队列 LinkedBlockingQueue</li>
<li>最多只有一个同步元素的 SynchronousQueue</li>
<li>优先队列 PriorityBlockingQueue</li>
</ul>
</li>
<li>threadFactory：线程工厂（给线程取名字）</li>
<li>handler：拒绝策略</li>
</ul>
<h4 id="工作方式"><a href="#工作方式" class="headerlink" title="工作方式"></a>工作方式</h4><ul>
<li>当一个任务传给线程池以后，可能有以下几种可能<ul>
<li>将任务分配给一个核心线程来执行</li>
<li>核心线程都在执行任务，将任务放到阻塞队列workQueue中等待被执行</li>
<li>阻塞队列满了，使用救急线程来执行任务<ul>
<li>救急线程用完以后，超过生存时间（keepAliveTime）后会被释放</li>
</ul>
</li>
<li>任务总数大于了 最大线程数（maximumPoolSize）与阻塞队列容量的最大值（workQueue.capacity），使用拒接策略</li>
</ul>
</li>
</ul>
<h4 id="拒绝策略"><a href="#拒绝策略" class="headerlink" title="拒绝策略"></a>拒绝策略</h4><p>如果线程到达 maximumPoolSize 仍然有新任务这时会执行<strong>拒绝策略</strong>。拒绝策略 jdk 提供了 4 种实现</p>
<p><img src="https://nyimapicture.oss-cn-beijing.aliyuncs.com/img/20201022194718.png" srcset="/img/loading.gif" alt=""></p>
<ul>
<li>AbortPolicy：让调用者抛出 RejectedExecutionException 异常，<strong>这是默认策略</strong></li>
<li>CallerRunsPolicy：让调用者运行任务</li>
<li>DiscardPolicy：放弃本次任务</li>
<li>DiscardOldestPolicy：放弃队列中最早的任务，本任务取而代之</li>
</ul>
<h4 id="使用-2"><a href="#使用-2" class="headerlink" title="使用"></a>使用</h4><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Demo1</span> </span>&#123;
   <span class="hljs-keyword">static</span> AtomicInteger threadId = <span class="hljs-keyword">new</span> AtomicInteger(<span class="hljs-number">0</span>);

   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      <span class="hljs-comment">// 手动创建线程池</span>
      <span class="hljs-comment">// 创建有界阻塞队列</span>
      ArrayBlockingQueue&lt;Runnable&gt; runnable = <span class="hljs-keyword">new</span> ArrayBlockingQueue&lt;Runnable&gt;(<span class="hljs-number">10</span>);
      <span class="hljs-comment">// 创建线程工厂</span>
      ThreadFactory threadFactory = <span class="hljs-keyword">new</span> ThreadFactory() &#123;
         <span class="hljs-meta">@Override</span>
         <span class="hljs-function"><span class="hljs-keyword">public</span> Thread <span class="hljs-title">newThread</span><span class="hljs-params">(Runnable r)</span> </span>&#123;
            Thread thread = <span class="hljs-keyword">new</span> Thread(r, <span class="hljs-string">"working_thread_"</span>+threadId.getAndIncrement());
            <span class="hljs-keyword">return</span> thread;
         &#125;
      &#125;;

      <span class="hljs-comment">// 手动创建线程池</span>
      <span class="hljs-comment">// 拒绝策略采用默认策略</span>
      ThreadPoolExecutor executor = <span class="hljs-keyword">new</span> ThreadPoolExecutor(<span class="hljs-number">5</span>, <span class="hljs-number">7</span>, <span class="hljs-number">10</span>, TimeUnit.SECONDS, runnable, threadFactory);

      <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">20</span>; i++) &#123;
         executor.execute(<span class="hljs-keyword">new</span> Runnable() &#123;
            <span class="hljs-meta">@Override</span>
            <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>&#123;
               System.out.println(Thread.currentThread());
               <span class="hljs-keyword">try</span> &#123;
                  Thread.sleep(<span class="hljs-number">100000</span>);
               &#125; <span class="hljs-keyword">catch</span> (InterruptedException e) &#123;
                  e.printStackTrace();
               &#125;
            &#125;
         &#125;);
      &#125;
   &#125;
&#125;</code></pre>





<h3 id="FixedThreadPool"><a href="#FixedThreadPool" class="headerlink" title="FixedThreadPool"></a>FixedThreadPool</h3><pre><code class="hljs java"><span class="hljs-keyword">public</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">TestFixedThreadPool</span> </span>&#123;
   <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">static</span> <span class="hljs-keyword">void</span> <span class="hljs-title">main</span><span class="hljs-params">(String[] args)</span> </span>&#123;
      <span class="hljs-comment">// 自定义线程工厂</span>
      ThreadFactory factory = <span class="hljs-keyword">new</span> ThreadFactory() &#123;
         AtomicInteger atomicInteger = <span class="hljs-keyword">new</span> AtomicInteger(<span class="hljs-number">1</span>);

         <span class="hljs-meta">@Override</span>
         <span class="hljs-function"><span class="hljs-keyword">public</span> Thread <span class="hljs-title">newThread</span><span class="hljs-params">(Runnable r)</span> </span>&#123;
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> Thread(r, <span class="hljs-string">"myThread_"</span> + atomicInteger.getAndIncrement());
         &#125;
      &#125;;

      <span class="hljs-comment">// 创建核心线程数量为2的线程池</span>
      <span class="hljs-comment">// 通过 ThreadFactory可以给线程添加名字</span>

      ExecutorService executorService = Executors.newFixedThreadPool(<span class="hljs-number">2</span>, factory);

      <span class="hljs-comment">// 任务</span>
      Runnable runnable = <span class="hljs-keyword">new</span> Runnable() &#123;
         <span class="hljs-meta">@Override</span>
         <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>&#123;
            System.out.println(Thread.currentThread().getName());
            System.out.println(<span class="hljs-string">"this is fixedThreadPool"</span>);
         &#125;
      &#125;;
      
      executorService.execute(runnable);
   &#125;
&#125;</code></pre>

<p>固定大小的线程池可以传入两个参数</p>
<ul>
<li>核心线程数：nThreads</li>
<li>线程工厂：threadFactory</li>
</ul>
<p>内部调用的构造方法</p>
<pre><code class="hljs java">ThreadPoolExecutor(nThreads, nThreads,
                              <span class="hljs-number">0L</span>, TimeUnit.MILLISECONDS,
                              <span class="hljs-keyword">new</span> LinkedBlockingQueue&lt;Runnable&gt;(),
                              threadFactory);</code></pre>



<h3 id="CachedThreadPool"><a href="#CachedThreadPool" class="headerlink" title="CachedThreadPool"></a>CachedThreadPool</h3><pre><code class="hljs java">ExecutorService executorService = Executors.newCachedThreadPool();</code></pre>

<p><strong>内部构造方法</strong></p>
<pre><code class="hljs java">ThreadPoolExecutor(<span class="hljs-number">0</span>, Integer.MAX_VALUE,
                              <span class="hljs-number">60L</span>, TimeUnit.SECONDS,
                              <span class="hljs-keyword">new</span> SynchronousQueue&lt;Runnable&gt;());</code></pre>

<ul>
<li><p>没有核心线程，最大线程数为Integer.MAX_VALUE，<strong>所有创建的线程都是救急线程</strong>，空闲时生存时间为60秒</p>
</li>
<li><p>阻塞队列使用的是SynchronousQueue</p>
<ul>
<li><strong>SynchronousQueue</strong>是一种特殊的队列<ul>
<li><strong>没有容量</strong>，没有线程来取是放不进去的</li>
<li>只有当线程取任务时，才会将任务放入该阻塞队列中</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="SingleThread"><a href="#SingleThread" class="headerlink" title="SingleThread"></a>SingleThread</h3><pre><code class="hljs java">ExecutorService service = Executors.newSingleThreadExecutor();</code></pre>

<p><strong>内部构造方法</strong></p>
<pre><code class="hljs java"><span class="hljs-keyword">new</span> FinalizableDelegatedExecutorService
    (<span class="hljs-keyword">new</span> ThreadPoolExecutor(<span class="hljs-number">1</span>, <span class="hljs-number">1</span>,
                            <span class="hljs-number">0L</span>, TimeUnit.MILLISECONDS,
                            <span class="hljs-keyword">new</span> LinkedBlockingQueue&lt;Runnable&gt;()));</code></pre>

<p>内部调用了<strong>new ThreadPoolExecutor</strong>的构造方法，传入的corePoolSize和maximumPoolSize都为1。然后将该对象传给了FinalizableDelegatedExecutorService。该类修饰了ThreadPoolExecutor，让外部无法调用ThreadPoolExecutor内部的某些方法来修改所创建的线程池的大小。</p>
<h4 id="几个注意"><a href="#几个注意" class="headerlink" title="几个注意"></a>几个注意</h4><ul>
<li><p>SingleThread和自己创建一个线程来运行多个任务的区别</p>
<ul>
<li>当线程正在执行的任务发生错误时，如果是自己创建的线程，该任务和剩余的任务就无法再继续运行下去。而SingleThread会创建一个新线程，继续执行任务队列中剩余的任务。</li>
</ul>
</li>
<li><p>SingleThread和newFixedThreadPool(1)的区别</p>
<ul>
<li>newFixedThreadPool(1)传值为1，可以将FixedThreadPool强转为ThreadPoolExecutor，然后通过setCorePoolSize改变核心线程数</li>
</ul>
<pre><code class="hljs java"><span class="hljs-comment">// 强转为ThreadPoolExecutor</span>
ThreadPoolExecutor threadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(<span class="hljs-number">1</span>);
<span class="hljs-comment">// 改变核心线程数</span>
threadPool.setCorePoolSize(<span class="hljs-number">2</span>);</code></pre>
<ul>
<li>而SingleThread无法修改核心线程数</li>
</ul>
</li>
</ul>
<h3 id="执行任务"><a href="#执行任务" class="headerlink" title="执行任务"></a>执行任务</h3><h4 id="execute-方法"><a href="#execute-方法" class="headerlink" title="execute()方法"></a>execute()方法</h4><pre><code class="hljs java">execute(Runnable command)</code></pre>

<p>传入一个Runnable对象，执行其中的run方法</p>
<p><strong>源码解析</strong></p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">execute</span><span class="hljs-params">(Runnable command)</span> </span>&#123;
    <span class="hljs-keyword">if</span> (command == <span class="hljs-keyword">null</span>)
        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> NullPointerException();

    <span class="hljs-comment">// 获取ctl</span>
    <span class="hljs-keyword">int</span> c = ctl.get();
    
    <span class="hljs-comment">// 判断当前启用的线程数是否小于核心线程数</span>
    <span class="hljs-keyword">if</span> (workerCountOf(c) &lt; corePoolSize) &#123;
        <span class="hljs-comment">// 为该任务分配线程</span>
        <span class="hljs-keyword">if</span> (addWorker(command, <span class="hljs-keyword">true</span>))
            <span class="hljs-comment">// 分配成功就返回</span>
            <span class="hljs-keyword">return</span>;
        
        <span class="hljs-comment">// 分配失败再次获取ctl</span>
        c = ctl.get();
    &#125;
    
    <span class="hljs-comment">// 分配和信息线程失败以后</span>
    <span class="hljs-comment">// 如果池状态为RUNNING并且插入到任务队列成功</span>
    <span class="hljs-keyword">if</span> (isRunning(c) &amp;&amp; workQueue.offer(command)) &#123;
        
        <span class="hljs-comment">// 双重检测，可能在添加后线程池状态变为了非RUNNING</span>
        <span class="hljs-keyword">int</span> recheck = ctl.get();
        
        <span class="hljs-comment">// 如果池状态为非RUNNING，则不会执行新来的任务</span>
        <span class="hljs-comment">// 将该任务从阻塞队列中移除</span>
        <span class="hljs-keyword">if</span> (! isRunning(recheck) &amp;&amp; remove(command))
            <span class="hljs-comment">// 调用拒绝策略，拒绝该任务的执行</span>
            reject(command);
        
        <span class="hljs-comment">// 如果没有正在运行的线程</span>
        <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (workerCountOf(recheck) == <span class="hljs-number">0</span>)
            <span class="hljs-comment">// 就创建新线程来执行该任务</span>
            addWorker(<span class="hljs-keyword">null</span>, <span class="hljs-keyword">false</span>);
    &#125;
    
    <span class="hljs-comment">// 如果添加失败了（任务队列已满），就调用拒绝策略</span>
    <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (!addWorker(command, <span class="hljs-keyword">false</span>))
        reject(command);
&#125;</code></pre>

<p>其中调用了<strong>addWoker()</strong>方法，再看看看这个方法</p>
<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">addWorker</span><span class="hljs-params">(Runnable firstTask, <span class="hljs-keyword">boolean</span> core)</span> </span>&#123;
    retry:
    <span class="hljs-keyword">for</span> (;;) &#123;
        <span class="hljs-keyword">int</span> c = ctl.get();
        <span class="hljs-keyword">int</span> rs = runStateOf(c);

        <span class="hljs-comment">// Check if queue empty only if necessary.</span>
        <span class="hljs-comment">// 如果池状态为非RUNNING状态、线程池为SHUTDOWN且该任务为空 或者阻塞队列中已经有任务</span>
        <span class="hljs-keyword">if</span> (rs &gt;= SHUTDOWN &amp;&amp;
            ! (rs == SHUTDOWN &amp;&amp;
               firstTask == <span class="hljs-keyword">null</span> &amp;&amp;
               ! workQueue.isEmpty()))
            <span class="hljs-comment">// 创建新线程失败</span>
            <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;

        <span class="hljs-keyword">for</span> (;;) &#123;
            <span class="hljs-comment">// 获得当前工作线程数</span>
            <span class="hljs-keyword">int</span> wc = workerCountOf(c);

            <span class="hljs-comment">// 参数中 core 为true</span>
            <span class="hljs-comment">// CAPACITY 为 1 &lt;&lt; COUNT_BITS-1，一般不会超过</span>
            <span class="hljs-comment">// 如果工作线程数大于了核心线程数，则创建失败</span>
            <span class="hljs-keyword">if</span> (wc &gt;= CAPACITY ||
                wc &gt;= (core ? corePoolSize : maximumPoolSize))
                <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;
            <span class="hljs-comment">// 通过CAS操作改变c的值</span>
            <span class="hljs-keyword">if</span> (compareAndIncrementWorkerCount(c))
                <span class="hljs-comment">// 更改成功就跳出多重循环，且不再运行循环</span>
                <span class="hljs-keyword">break</span> retry;
            <span class="hljs-comment">// 更改失败，重新获取ctl的值</span>
            c = ctl.get();  <span class="hljs-comment">// Re-read ctl</span>
            <span class="hljs-keyword">if</span> (runStateOf(c) != rs)
                <span class="hljs-comment">// 跳出多重循环，且重新进入循环</span>
                <span class="hljs-keyword">continue</span> retry;
            <span class="hljs-comment">// else CAS failed due to workerCount change; retry inner loop</span>
        &#125;
    &#125;

    <span class="hljs-comment">// 用于标记work中的任务是否成功执行</span>
    <span class="hljs-keyword">boolean</span> workerStarted = <span class="hljs-keyword">false</span>;
    <span class="hljs-comment">// 用于标记worker是否成功加入了线程池中</span>
    <span class="hljs-keyword">boolean</span> workerAdded = <span class="hljs-keyword">false</span>;
    Worker w = <span class="hljs-keyword">null</span>;
    <span class="hljs-keyword">try</span> &#123;
        <span class="hljs-comment">// 创建新线程来执行任务</span>
        w = <span class="hljs-keyword">new</span> Worker(firstTask);
        <span class="hljs-keyword">final</span> Thread t = w.thread;
        <span class="hljs-keyword">if</span> (t != <span class="hljs-keyword">null</span>) &#123;
            <span class="hljs-keyword">final</span> ReentrantLock mainLock = <span class="hljs-keyword">this</span>.mainLock;
            <span class="hljs-comment">// 加锁</span>
            mainLock.lock();
            <span class="hljs-keyword">try</span> &#123;
                <span class="hljs-comment">// Recheck while holding lock.</span>
                <span class="hljs-comment">// Back out on ThreadFactory failure or if</span>
                <span class="hljs-comment">// shut down before lock acquired.</span>
                <span class="hljs-comment">// 加锁的同时再次检测</span>
                <span class="hljs-comment">// 避免在释放锁之前调用了shut down</span>
                <span class="hljs-keyword">int</span> rs = runStateOf(ctl.get());

                <span class="hljs-keyword">if</span> (rs &lt; SHUTDOWN ||
                    (rs == SHUTDOWN &amp;&amp; firstTask == <span class="hljs-keyword">null</span>)) &#123;
                    <span class="hljs-keyword">if</span> (t.isAlive()) <span class="hljs-comment">// precheck that t is startable</span>
                        <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> IllegalThreadStateException();
                    <span class="hljs-comment">// 将线程添加到线程池中</span>
                    workers.add(w);
                    <span class="hljs-keyword">int</span> s = workers.size();
                    <span class="hljs-keyword">if</span> (s &gt; largestPoolSize)
                        largestPoolSize = s;
                    <span class="hljs-comment">// 添加成功标志位变为true</span>
                    workerAdded = <span class="hljs-keyword">true</span>;
                &#125;
            &#125; <span class="hljs-keyword">finally</span> &#123;
                mainLock.unlock();
            &#125;
            <span class="hljs-comment">// 如果worker成功加入了线程池，就执行其中的任务</span>
            <span class="hljs-keyword">if</span> (workerAdded) &#123;
                t.start();
                <span class="hljs-comment">// 启动成功</span>
                workerStarted = <span class="hljs-keyword">true</span>;
            &#125;
        &#125;
    &#125; <span class="hljs-keyword">finally</span> &#123;
        <span class="hljs-comment">// 如果执行失败</span>
        <span class="hljs-keyword">if</span> (! workerStarted)
            <span class="hljs-comment">// 调用添加失败的函数</span>
            addWorkerFailed(w);
    &#125;
    <span class="hljs-keyword">return</span> workerStarted;
&#125;</code></pre>





<h4 id="submit-方法"><a href="#submit-方法" class="headerlink" title="submit()方法"></a>submit()方法</h4><pre><code class="hljs java"><span class="hljs-function">Future&lt;T&gt; <span class="hljs-title">submit</span><span class="hljs-params">(Callable&lt;T&gt; task)</span></span></code></pre>

<p>传入一个Callable对象，用Future来<strong>捕获返回值</strong></p>
<p><strong>使用</strong></p>
<pre><code class="hljs java"><span class="hljs-comment">// 通过submit执行Callable中的call方法</span>
<span class="hljs-comment">// 通过Future来捕获返回值</span>
Future&lt;String&gt; future = threadPool.submit(<span class="hljs-keyword">new</span> Callable&lt;String&gt;() &#123;
   <span class="hljs-meta">@Override</span>
   <span class="hljs-function"><span class="hljs-keyword">public</span> String <span class="hljs-title">call</span><span class="hljs-params">()</span> <span class="hljs-keyword">throws</span> Exception </span>&#123;
      <span class="hljs-keyword">return</span> <span class="hljs-string">"hello submit"</span>;
   &#125;
&#125;);

<span class="hljs-comment">// 查看捕获的返回值</span>
System.out.println(future.get());</code></pre>



<h3 id="停止"><a href="#停止" class="headerlink" title="停止"></a>停止</h3><h4 id="shutdown"><a href="#shutdown" class="headerlink" title="shutdown()"></a>shutdown()</h4><pre><code class="hljs java"><span class="hljs-comment">/**</span>
<span class="hljs-comment">* 将线程池的状态改为 SHUTDOWN</span>
<span class="hljs-comment">* 不再接受新任务，但是会将阻塞队列中的任务执行完</span>
<span class="hljs-comment">*/</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">shutdown</span><span class="hljs-params">()</span> </span>&#123;
    <span class="hljs-keyword">final</span> ReentrantLock mainLock = <span class="hljs-keyword">this</span>.mainLock;
    mainLock.lock();
    <span class="hljs-keyword">try</span> &#123;
        checkShutdownAccess();
        
        <span class="hljs-comment">// 修改线程池状态为 SHUTDOWN</span>
        advanceRunState(SHUTDOWN);
        
  		<span class="hljs-comment">// 中断空闲线程（没有执行任务的线程）</span>
        <span class="hljs-comment">// Idle：空闲的</span>
        interruptIdleWorkers();
        onShutdown(); <span class="hljs-comment">// hook for ScheduledThreadPoolExecutor</span>
    &#125; <span class="hljs-keyword">finally</span> &#123;
        mainLock.unlock();
    &#125;
    <span class="hljs-comment">// 尝试终结，不一定成功</span>
    <span class="hljs-comment">// </span>
    tryTerminate();
&#125;</code></pre>

<pre><code class="hljs java"><span class="hljs-function"><span class="hljs-keyword">final</span> <span class="hljs-keyword">void</span> <span class="hljs-title">tryTerminate</span><span class="hljs-params">()</span> </span>&#123;
    <span class="hljs-keyword">for</span> (;;) &#123;
        <span class="hljs-keyword">int</span> c = ctl.get();
        <span class="hljs-comment">// 终结失败的条件</span>
        <span class="hljs-comment">// 线程池状态为RUNNING</span>
        <span class="hljs-comment">// 线程池状态为 RUNNING SHUTDOWN STOP （状态值大于TIDYING）</span>
        <span class="hljs-comment">// 线程池状态为SHUTDOWN，但阻塞队列中还有任务等待执行</span>
        <span class="hljs-keyword">if</span> (isRunning(c) ||
            runStateAtLeast(c, TIDYING) ||
            (runStateOf(c) == SHUTDOWN &amp;&amp; ! workQueue.isEmpty()))
            <span class="hljs-keyword">return</span>;
        
        <span class="hljs-comment">// 如果活跃线程数不为0</span>
        <span class="hljs-keyword">if</span> (workerCountOf(c) != <span class="hljs-number">0</span>) &#123; <span class="hljs-comment">// Eligible to terminate</span>
            <span class="hljs-comment">// 中断空闲线程</span>
            interruptIdleWorkers(ONLY_ONE);
            <span class="hljs-keyword">return</span>;
        &#125;

        <span class="hljs-keyword">final</span> ReentrantLock mainLock = <span class="hljs-keyword">this</span>.mainLock;
        mainLock.lock();
        <span class="hljs-keyword">try</span> &#123;
            <span class="hljs-comment">// 处于可以终结的状态</span>
            <span class="hljs-comment">// 通过CAS将线程池状态改为TIDYING</span>
            <span class="hljs-keyword">if</span> (ctl.compareAndSet(c, ctlOf(TIDYING, <span class="hljs-number">0</span>))) &#123;
                <span class="hljs-keyword">try</span> &#123;
                    terminated();
                &#125; <span class="hljs-keyword">finally</span> &#123;
                    <span class="hljs-comment">// 通过CAS将线程池状态改为TERMINATED</span>
                    ctl.set(ctlOf(TERMINATED, <span class="hljs-number">0</span>));
                    termination.signalAll();
                &#125;
                <span class="hljs-keyword">return</span>;
            &#125;
        &#125; <span class="hljs-keyword">finally</span> &#123;
            mainLock.unlock();
        &#125;
        <span class="hljs-comment">// else retry on failed CAS</span>
    &#125;
&#125;</code></pre>



<h4 id="shutdownNow"><a href="#shutdownNow" class="headerlink" title="shutdownNow()"></a>shutdownNow()</h4><pre><code class="hljs java"><span class="hljs-comment">/**</span>
<span class="hljs-comment">* 将线程池的状态改为 STOP</span>
<span class="hljs-comment">* 不再接受新任务，也不会在执行阻塞队列中的任务</span>
<span class="hljs-comment">* 会将阻塞队列中未执行的任务返回给调用者</span>
<span class="hljs-comment">*/</span>
<span class="hljs-function"><span class="hljs-keyword">public</span> List&lt;Runnable&gt; <span class="hljs-title">shutdownNow</span><span class="hljs-params">()</span> </span>&#123;
    List&lt;Runnable&gt; tasks;
    <span class="hljs-keyword">final</span> ReentrantLock mainLock = <span class="hljs-keyword">this</span>.mainLock;
    mainLock.lock();
    <span class="hljs-keyword">try</span> &#123;
        checkShutdownAccess();
        
        <span class="hljs-comment">// 修改状态为STOP，不执行任何任务</span>
        advanceRunState(STOP);
        
        <span class="hljs-comment">// 中断所有线程</span>
        interruptWorkers();
        
        <span class="hljs-comment">// 将未执行的任务从队列中移除，然后返回给调用者</span>
        tasks = drainQueue();
    &#125; <span class="hljs-keyword">finally</span> &#123;
        mainLock.unlock();
    &#125;
    <span class="hljs-comment">// 尝试终结，一定会成功，因为阻塞队列为空了</span>
    tryTerminate();
    <span class="hljs-keyword">return</span> tasks;
&#125;</code></pre>


            </article>
            <hr>
            <div>
              <div class="post-metas mb-3">
                
                  <div class="post-meta mr-3">
                    <i class="iconfont icon-category"></i>
                    
                      <a class="hover-with-bg" href="/categories/Java/">Java</a>
                    
                  </div>
                
                
              </div>
              
                <p class="note note-warning">本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-sa/4.0/deed.zh" target="_blank" rel="nofollow noopener noopener">CC BY-SA 4.0 协议</a> ，转载请注明出处！</p>
              
              
                <div class="post-prevnext row">
                  <div class="post-prev col-6">
                    
                    
                      <a href="/2020/06/17/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E4%B8%8E%E7%AE%97%E6%B3%95/">
                        <i class="iconfont icon-arrowleft"></i>
                        <span class="hidden-mobile">Java数据结构与算法</span>
                        <span class="visible-mobile">Previous</span>
                      </a>
                    
                  </div>
                  <div class="post-next col-6">
                    
                    
                      <a href="/2020/06/07/Redis%E5%AD%A6%E4%B9%A0%E6%96%87%E6%A1%A3/">
                        <span class="hidden-mobile">Redis学习</span>
                        <span class="visible-mobile">Next</span>
                        <i class="iconfont icon-arrowright"></i>
                      </a>
                    
                  </div>
                </div>
              
            </div>

            
              <!-- Comments -->
              <div class="comments" id="comments">
                
                
  <div class="disqus" style="width:100%">
    <div id="disqus_thread"></div>
    <script type="text/javascript">
      function loadDisqus() {
        var disqus_config = function () {
          this.page.url = 'http://nyimac.gitee.io/2020/06/08/并发编程/';
          this.page.identifier = '/2020/06/08/并发编程/';
        };
        (function () {
          var d = document,
            s = d.createElement('script');
          s.src = '//' + '' + '.disqus.com/embed.js';
          s.setAttribute('data-timestamp', new Date());
          (d.head || d.body).appendChild(s);
        })();
      }
      createObserver(loadDisqus, 'disqus_thread');
    </script>
    <noscript>Please enable JavaScript to view the
      <a href="https://disqus.com/?ref_noscript" target="_blank" rel="nofollow noopener noopener">comments powered by Disqus.</a>
    </noscript>
  </div>


              </div>
            
          </div>
        </div>
      </div>
    </div>
    
      <div class="d-none d-lg-block col-lg-2 toc-container" id="toc-ctn">
        <div id="toc">
  <p class="toc-header"><i class="iconfont icon-list"></i>&nbsp;TOC</p>
  <div id="tocbot"></div>
</div>

      </div>
    
  </div>
</div>

<!-- Custom -->


    
  </main>

  
    <a id="scroll-top-button" href="#" role="button">
      <i class="iconfont icon-arrowup" aria-hidden="true"></i>
    </a>
  

  
    <div class="modal fade" id="modalSearch" tabindex="-1" role="dialog" aria-labelledby="ModalLabel"
     aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg" role="document">
    <div class="modal-content">
      <div class="modal-header text-center">
        <h4 class="modal-title w-100 font-weight-bold">Search</h4>
        <button type="button" id="local-search-close" class="close" data-dismiss="modal" aria-label="Close">
          <span aria-hidden="true">&times;</span>
        </button>
      </div>
      <div class="modal-body mx-3">
        <div class="md-form mb-5">
          <input type="text" id="local-search-input" class="form-control validate">
          <label data-error="x" data-success="v"
                 for="local-search-input">keyword</label>
        </div>
        <div class="list-group" id="local-search-result"></div>
      </div>
    </div>
  </div>
</div>
  

  

  

  <footer class="mt-5">
  <div class="text-center py-3">
    <div>
      <a href="https://hexo.io" target="_blank" rel="nofollow noopener"><span>Hexo</span></a>
      <i class="iconfont icon-love"></i>
      <a href="https://github.com/fluid-dev/hexo-theme-fluid" target="_blank" rel="nofollow noopener">
        <span>Fluid</span></a>
    </div>
    
  <div class="statistics">
    
    

    
      
        <!-- 不蒜子统计PV -->
        <span id="busuanzi_container_site_pv" style="display: none">
            总访问量 
            <span id="busuanzi_value_site_pv"></span>
             次
          </span>
      
      
        <!-- 不蒜子统计UV -->
        <span id="busuanzi_container_site_uv" style="display: none">
            总访客数 
            <span id="busuanzi_value_site_uv"></span>
             人
          </span>
      
    
  </div>


    

    
  </div>
</footer>

<!-- SCRIPTS -->
<script  src="https://cdn.staticfile.org/jquery/3.4.1/jquery.min.js" ></script>
<script  src="https://cdn.staticfile.org/twitter-bootstrap/4.4.1/js/bootstrap.min.js" ></script>
<script  src="/js/debouncer.js" ></script>
<script  src="/js/main.js" ></script>

<!-- Plugins -->


  
    <script  src="/js/lazyload.js" ></script>
  



  <script defer src="https://cdn.staticfile.org/clipboard.js/2.0.6/clipboard.min.js" ></script>
  <script  src="/js/clipboard-use.js" ></script>



  <script defer src="https://busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js" ></script>





  <script  src="https://cdn.staticfile.org/tocbot/4.11.1/tocbot.min.js" ></script>
  <script>
    $(document).ready(function () {
      var boardCtn = $('#board-ctn');
      var boardTop = boardCtn.offset().top;

      tocbot.init({
        tocSelector: '#tocbot',
        contentSelector: 'article.markdown-body',
        headingSelector: 'h1,h2,h3,h4,h5,h6',
        linkClass: 'tocbot-link',
        activeLinkClass: 'tocbot-active-link',
        listClass: 'tocbot-list',
        isCollapsedClass: 'tocbot-is-collapsed',
        collapsibleClass: 'tocbot-is-collapsible',
        collapseDepth: 0,
        scrollSmooth: true,
        headingsOffset: -boardTop
      });
      if ($('.toc-list-item').length > 0) {
        $('#toc').css('visibility', 'visible');
      }
    });
  </script>



  <script  src="https://cdn.staticfile.org/typed.js/2.0.11/typed.min.js" ></script>
  <script>
    var typed = new Typed('#subtitle', {
      strings: [
        '  ',
        "Java并发&nbsp;",
      ],
      cursorChar: "_",
      typeSpeed: 70,
      loop: false,
    });
    typed.stop();
    $(document).ready(function () {
      $(".typed-cursor").addClass("h2");
      typed.start();
    });
  </script>



  <script  src="https://cdn.staticfile.org/anchor-js/4.2.2/anchor.min.js" ></script>
  <script>
    anchors.options = {
      placement: "right",
      visible: "hover",
      
    };
    var el = "h1,h2,h3,h4,h5,h6".split(",");
    var res = [];
    for (item of el) {
      res.push(".markdown-body > " + item)
    }
    anchors.add(res.join(", "))
  </script>



  <script  src="/js/local-search.js" ></script>
  <script>
    var path = "/local-search.xml";
    var inputArea = document.querySelector("#local-search-input");
    inputArea.onclick = function () {
      searchFunc(path, 'local-search-input', 'local-search-result');
      this.onclick = null
    }
  </script>



  <script  src="https://cdn.staticfile.org/fancybox/3.5.7/jquery.fancybox.min.js" ></script>
  <link  rel="stylesheet" href="https://cdn.staticfile.org/fancybox/3.5.7/jquery.fancybox.min.css" />

  <script>
    $('#post img:not(.no-zoom img, img[no-zoom]), img[zoom]').each(
      function () {
        var element = document.createElement('a');
        $(element).attr('data-fancybox', 'images');
        $(element).attr('href', $(this).attr('src'));
        $(this).wrap(element);
      }
    );
  </script>

















  
    <!-- Baidu Analytics -->
    <script defer>
      var _hmt = _hmt || [];
      (function () {
        var hm = document.createElement("script");
        hm.src = "https://hm.baidu.com/hm.js?ba41ec605b9b7320e120275462e4035b";
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(hm, s);
      })();
    </script>
  

  

  

  

  

  





</body>
</html>
